home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / The Hacks / GrowBoxDock / Sources / TextFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-30  |  91.2 KB  |  3,269 lines

  1. /*
  2.     File:        TextFile.c
  3.  
  4.     Contains:    Text file support for simple text application.
  5.  
  6.     Version:    Mac OS X
  7.  
  8.     Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  9.                 ("Apple") in consideration of your agreement to the following terms, and your
  10.                 use, installation, modification or redistribution of this Apple software
  11.                 constitutes acceptance of these terms.  If you do not agree with these terms,
  12.                 please do not use, install, modify or redistribute this Apple software.
  13.  
  14.                 In consideration of your agreement to abide by the following terms, and subject
  15.                 to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
  16.                 copyrights in this original Apple software (the "Apple Software"), to use,
  17.                 reproduce, modify and redistribute the Apple Software, with or without
  18.                 modifications, in source and/or binary forms; provided that if you redistribute
  19.                 the Apple Software in its entirety and without modifications, you must retain
  20.                 this notice and the following text and disclaimers in all such redistributions of
  21.                 the Apple Software.  Neither the name, trademarks, service marks or logos of
  22.                 Apple Computer, Inc. may be used to endorse or promote products derived from the
  23.                 Apple Software without specific prior written permission from Apple.  Except as
  24.                 expressly stated in this notice, no other rights or licenses, express or implied,
  25.                 are granted by Apple herein, including but not limited to any patent rights that
  26.                 may be infringed by your derivative works or by other works in which the Apple
  27.                 Software may be incorporated.
  28.  
  29.                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  30.                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  31.                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  32.                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  33.                 COMBINATION WITH YOUR PRODUCTS.
  34.  
  35.                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  36.                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  37.                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  38.                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  39.                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  40.                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  41.                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  
  43.     Copyright © 1993-2001 Apple Computer, Inc., All Rights Reserved
  44. */
  45.  
  46. #include "MacIncludes.h"
  47.  
  48. #include "TextFile.h"
  49.  
  50. // --------------------------------------------------------------------------------------------------------------
  51. // INTERNAL DEFINES
  52. // --------------------------------------------------------------------------------------------------------------
  53. #define kOnePageWidth         600            // preferred width of a window
  54. #define kMargins            4            // margins in window
  55. #define kPrintMargins        8            // margins in printing window
  56.  
  57. #define kPictureBase        1000        // resource base ID for PICTs in documents
  58. #define kSoundBase            10000        // resource base ID for 'snd 's in documents
  59.  
  60. // resources for controling printing
  61. #define kFormResource        'form'
  62.     #define kFormFeed            0x00000001    // form feed after this line
  63.  
  64. #define    kContentsListID        10000        // resource ID for STR# contents menu         
  65.  
  66. // some memory requirements
  67. #define kPrefBufferSize        90*1024
  68. #define kMinBufferSize        5*1024
  69.  
  70. #define kDeleteKey            8
  71. #define kForwardDeleteKey    0x75
  72.  
  73.  
  74. extern pascal unsigned char * AsmClikLoop(TEPtr pTE);
  75.  
  76. #define ABS(n)                (((n) < 0) ? -(n) : (n))
  77.  
  78. // --------------------------------------------------------------------------------------------------------------
  79. // GLOBALS USED ONLY BY THESE ROUTINES
  80. // --------------------------------------------------------------------------------------------------------------
  81.  
  82. // These variables are used for speech, notice that on purpose we only
  83. // support a single channel at a time.
  84. static SpeechChannel    gSpeechChannel = nil;        
  85. static VoiceSpec        gCurrentVoice;
  86. static Ptr                gSpeakPtr = nil;
  87. static Boolean            gAddedVoices = false;
  88.  
  89. static Str31            gPictMarker1, gPictMarker2;
  90.  
  91. // Other globals local to this file (and TextDrag.c)
  92. Boolean                    gTE6Version = false;
  93.  
  94. // External globals for AppleScript
  95. extern    ComponentInstance gOSAComponent;
  96.  
  97. // --------------------------------------------------------------------------------------------------------------
  98. // EXTERNAL FUNCTIONS
  99. // --------------------------------------------------------------------------------------------------------------
  100.  
  101. extern OSStatus    TextDragTracking(WindowPtr pWindow, void *pData, DragReference theDragRef, short message);
  102. extern OSStatus    TextDragReceive(WindowPtr pWindow, void *refCon, DragReference theDragRef);
  103. extern Boolean    DragText(WindowPtr pWindow, void *pData, EventRecord *pEvent, RgnHandle hilightRgn);
  104.  
  105. // --------------------------------------------------------------------------------------------------------------
  106. // INTERNAL ROUTINES
  107. // --------------------------------------------------------------------------------------------------------------
  108.  
  109. static void TextAddContentsMenu(WindowDataPtr pData);
  110. static void TextRemoveContentsMenu(WindowDataPtr pData);
  111. static OSStatus TextGetContentsListItem(WindowDataPtr pData, short itemNum, StringPtr menuStr, StringPtr searchStr, short *totalItems);
  112. static OSStatus TextAdjustContentsMenu(WindowDataPtr pData);
  113. static void TextAddVoices();
  114.  
  115. // --------------------------------------------------------------------------------------------------------------
  116. // UNDO UTILITY FUNCTIONS
  117. // --------------------------------------------------------------------------------------------------------------
  118. OSStatus SaveCurrentUndoState(WindowDataPtr pData, short newCommandID)
  119. {
  120.     OSStatus        anErr = noErr;
  121.     TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  122.     
  123.     // this is only a new command if:
  124.     //    the command ID is different
  125.     // OR
  126.     //    the selection is a range, and not equal to the old one
  127.     if     (
  128.             ( ((TextDataPtr) pData)->prevCommandID != newCommandID )
  129.         ||
  130.             (
  131.                 ( (**hTE).selStart != (**hTE).selEnd )
  132.             ||
  133.                 ( ABS((**hTE).selStart - ((TextDataPtr) pData)->prevSelStart) > 1 )
  134.             )
  135.         )
  136.         {
  137.         Handle        tempHandle;
  138.         Size        tempSize;
  139.         
  140.         // if we don't have save handles, make em!
  141.         if (!((TextDataPtr) pData)->prevText)
  142.             {
  143.             ((TextDataPtr) pData)->prevText = NewHandle(0);
  144.             anErr = MemError();
  145.             nrequire(anErr, MakeTextSave);
  146.             }
  147.         if  (!((TextDataPtr) pData)->prevStyle) 
  148.             {
  149.             ((TextDataPtr) pData)->prevStyle = NewHandle(0);
  150.             anErr = MemError();
  151.             nrequire(anErr, MakeStyleSave);
  152.             }
  153.             
  154.         // grow the save handles and block move into them
  155.         tempHandle = (**hTE).hText;
  156.         tempSize = GetHandleSize(tempHandle);
  157.         SetHandleSize( ((TextDataPtr) pData)->prevText, tempSize);
  158.         anErr = MemError();
  159.         nrequire(anErr, GrowTextSave);
  160.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevText, tempSize );
  161.         
  162.         tempHandle = (Handle) TEGetStyleHandle(hTE);
  163.         tempSize = GetHandleSize(tempHandle);
  164.         SetHandleSize( ((TextDataPtr) pData)->prevStyle, tempSize);
  165.         anErr = MemError();
  166.         nrequire(anErr, GrowTextSave);
  167.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevStyle, tempSize );
  168.  
  169.         // save length
  170.         ((TextDataPtr) pData)->prevLength = (**hTE).teLength;
  171.         ((TextDataPtr) pData)->beforeSelStart = (**hTE).selStart;
  172.         ((TextDataPtr) pData)->beforeSelEnd = (**hTE).selEnd;
  173.         }
  174.     
  175.     // save start and end of the selection
  176.     ((TextDataPtr) pData)->prevSelStart = (**hTE).selStart;
  177.  
  178.     // if we didn't have any problems, then we can undo this operation
  179.     ((TextDataPtr) pData)->prevCommandID = newCommandID;
  180.     return(noErr);
  181.     
  182. // EXCEPTION HANDLING
  183. GrowTextSave:
  184. MakeStyleSave:
  185. MakeTextSave:
  186.     // can't undo because of an error 
  187.     // (at some point might warn the user, but that's probably more annoying)
  188.     ((TextDataPtr) pData)->prevCommandID = cNull;
  189.     
  190.     return(anErr);
  191.     
  192. } // SaveCurrentUndoState
  193.  
  194. // --------------------------------------------------------------------------------------------------------------
  195. static void PerformUndo(WindowDataPtr pData)
  196. {
  197.     if (((TextDataPtr) pData)->prevCommandID != cNull)
  198.         {
  199.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  200.         long        tempLong;
  201.  
  202.         // undo text
  203.         tempLong = (long) (**hTE).hText;
  204.         (**hTE).hText = ((TextDataPtr) pData)->prevText;
  205.         ((TextDataPtr) pData)->prevText = (Handle)tempLong;
  206.  
  207.         // undo length
  208.         tempLong = (long) (**hTE).teLength;
  209.         (**hTE).teLength = ((TextDataPtr) pData)->prevLength;
  210.         ((TextDataPtr) pData)->prevLength = tempLong;
  211.  
  212.         // undo style
  213.         tempLong = (long) TEGetStyleHandle(hTE);
  214.         HandToHand((Handle*)&tempLong);
  215.         TESetStyleHandle( (TEStyleHandle) ((TextDataPtr) pData)->prevStyle, hTE);
  216.         ((TextDataPtr) pData)->prevStyle = (Handle)tempLong;
  217.         
  218.         // undo selection
  219.             {
  220.             short    start, end;
  221.             
  222.             start = ((TextDataPtr) pData)->beforeSelStart;
  223.             end = ((TextDataPtr) pData)->beforeSelEnd;
  224.             TESetSelect(start, end, hTE);
  225.             }
  226.         }
  227.         
  228. } // PerformUndo
  229.  
  230.  
  231. // --------------------------------------------------------------------------------------------------------------
  232. // TEXT EDIT UTILITY FUNCTIONS
  233. // --------------------------------------------------------------------------------------------------------------
  234. static long CalculateTextEditHeight(TEHandle hTE)
  235. {
  236.     long    result;
  237.     short    length;
  238.     
  239.     result = TEGetHeight(32767, 0, hTE);
  240.     length = (**hTE).teLength;
  241.     
  242.     // Text Edit doesn't return the height of the last character, if that
  243.     // character is a <cr>.  So if we see that, we go grab the height of
  244.     // that last character and add it into the total height.
  245.     if ( (length) && ( (*(**hTE).hText)[length-1] == '\n') )
  246.         {
  247.         TextStyle    theStyle;
  248.         short        theHeight;
  249.         short        theAscent;
  250.  
  251.         TEGetStyle(length, &theStyle, &theHeight, &theAscent, hTE);
  252.         result += theHeight;
  253.         }
  254.  
  255.     return result;
  256.  
  257. } // CalculateTextEditHeight
  258.  
  259. // --------------------------------------------------------------------------------------------------------------
  260. void AdjustTE(WindowDataPtr pData, Boolean doScroll)
  261. {
  262.     TEPtr        te;
  263.     short        value;
  264.     short        prevValue;
  265.     
  266.     te = *((TextDataPtr) pData)->hTE;
  267.     prevValue = GetControlValue(pData->vScroll);
  268.     value = te->viewRect.top - te->destRect.top;
  269.     SetControlValue(pData->vScroll, value);
  270.  
  271.     te = *((TextDataPtr) pData)->hTE;
  272.     if (doScroll)
  273.         {
  274.         short    hScroll = te->viewRect.left - te->destRect.left;
  275.         short    vScroll = value - prevValue;
  276.         if ((hScroll != 0) || (vScroll != 0))
  277.             TEScroll(hScroll, vScroll, ((TextDataPtr) pData)->hTE);
  278.         }
  279.     
  280.             
  281. } // AdjustTE
  282.  
  283. // --------------------------------------------------------------------------------------------------------------
  284.  
  285. static void RecalcTE(WindowDataPtr pData, Boolean doInval)
  286. {
  287.     Rect    viewRect, destRect;
  288.     short    oldOffset;
  289.     
  290.     destRect = (**((TextDataPtr) pData)->hTE).destRect;
  291.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  292.     oldOffset = destRect.top - viewRect.top;
  293.     
  294.     viewRect = pData->contentRect;
  295.  
  296.     InsetRect(&viewRect, kMargins, kMargins);
  297.     destRect = viewRect;
  298.     
  299.     OffsetRect(&destRect, 0, oldOffset);
  300.     
  301.     (**((TextDataPtr) pData)->hTE).viewRect = viewRect;
  302.     (**((TextDataPtr) pData)->hTE).destRect = destRect;
  303.     
  304.     TECalText(((TextDataPtr) pData)->hTE);
  305.  
  306.     if (doInval)
  307.     {
  308.         WindowPtr window = FrontWindow();
  309.         if ( window )
  310.         {
  311.             Rect        rect;
  312.  
  313.             GetPortBounds( GetWindowPort( window ), &rect );
  314.             InvalWindowRect( window, &rect );
  315.         }
  316.     }
  317. } // RecalcTE
  318.  
  319. // --------------------------------------------------------------------------------------------------------------
  320.  
  321. pascal TEClickLoopUPP GetOldClickLoop(void)
  322. {
  323. // AEC, changed to obtain application data from the WRefCon
  324.      TextDataPtr    textData;
  325.  
  326.      textData = (TextDataPtr) GetWRefCon(GetWindowFromPort(GetQDGlobalsThePort()));
  327.      return textData->docClick;     
  328. } // GetOldClikLoop
  329.  
  330. pascal void TextClickLoop(void)
  331. {    
  332.     WindowPtr    window;
  333.     RgnHandle    region;
  334.     Rect        bounds;
  335.     TextDataPtr textData;
  336.  
  337.     window = FrontWindow();
  338.     region = NewRgn();
  339.     GetClip(region);                    /* save clip */
  340.     ClipRect(GetWindowPortBounds(window, &bounds));
  341.     
  342. // AEC, changed to obtain application data from the WRefCon
  343.     textData = (TextDataPtr) GetWRefCon(window);
  344.      
  345.     textData->insideClickLoop = true;
  346.         AdjustScrollBars(window, false, false, nil);
  347.     textData->insideClickLoop = false;
  348.  
  349.     SetClip(region);                    /* restore clip */
  350.     DisposeRgn(region);
  351.  
  352. } // TextClickLoop
  353.  
  354. static pascal Boolean CClikLoop(TEPtr pTE)
  355. {
  356.     InvokeTEClickLoopUPP(pTE, GetOldClickLoop());
  357.     
  358.     TextClickLoop();
  359.     
  360.     return(true);
  361.     
  362. } // CClikLoop
  363.  
  364. // --------------------------------------------------------------------------------------------------------------
  365. pascal void MyDrawHook ( unsigned short offset, unsigned short textLen,
  366.      Ptr textPtr, TEPtr tePtr, TEHandle teHdl )
  367. {
  368. #pragma unused    ( teHdl, tePtr)
  369.  
  370.     register short            drawLen = 0;
  371.     register Ptr            drawPtr;
  372.     
  373.     drawPtr = textPtr + (long)offset;
  374.     
  375.     while ( textLen--)
  376.         {
  377.         if ( *drawPtr == (char)0xCA &&
  378.              ( *(drawPtr - 1) == 0x0D || *tePtr->hText == textPtr) )
  379.             {
  380.             DrawText( textPtr, offset, drawLen);
  381.             DrawChar( '\040');
  382.             offset += drawLen + 1;
  383.             drawLen = 0;
  384.             }
  385.         else
  386.             {
  387.             ++drawLen;
  388.             }
  389.         ++drawPtr;
  390.         }
  391.     
  392.     DrawText( textPtr, offset, drawLen);
  393.     
  394. } // MyDrawHook
  395.  
  396.  
  397. // --------------------------------------------------------------------------------------------------------------
  398.  
  399. static void DisposeOfSpeech(Boolean throwAway)
  400. {
  401.     if (gSpeechChannel)
  402.         {
  403.         (void) StopSpeech( gSpeechChannel );
  404.         if (throwAway)
  405.             {
  406.             (void) DisposeSpeechChannel( gSpeechChannel );
  407.             gSpeechChannel = nil;
  408.             }
  409.         }
  410.     if (gSpeakPtr)
  411.         {
  412.         DisposePtr(gSpeakPtr);
  413.         gSpeakPtr = nil;
  414.         }
  415.         
  416. } // DisposeOfSpeech
  417.  
  418. // --------------------------------------------------------------------------------------------------------------
  419.  
  420. static Boolean FindNextPicture(Handle textHandle, short *offset, short *delta)
  421. {
  422.     long result;
  423.     
  424.     // marker at begining of document means a picture there
  425.     if ( (*offset == 0) && ( (*(unsigned char*)(*textHandle + (*offset))) == 0xCA) )
  426.         {
  427.         *delta = 1;
  428.         return true;
  429.         }
  430.  
  431.     if (gPictMarker1[0] != 0)
  432.         {
  433.         result = Munger(textHandle, *offset, &gPictMarker1[1], gPictMarker1[0], nil, 0);
  434.         if (result >= 0)
  435.             {
  436.             *offset = result;
  437.             *delta = gPictMarker1[0];
  438.             return true;
  439.             }
  440.         }
  441.  
  442.     if (gPictMarker2[0] != 0)
  443.         {
  444.         result = Munger(textHandle, *offset, &gPictMarker2[1], gPictMarker2[0], nil, 0);
  445.         if (result >= 0)
  446.             {
  447.             *offset = result;
  448.             *delta = gPictMarker2[0];
  449.             return true;
  450.             }
  451.         }
  452.         
  453.     *delta = 1;
  454.     return false;
  455.     
  456. } // FindNextPicture
  457.  
  458. // --------------------------------------------------------------------------------------------------------------
  459. static Boolean LineHasPageBreak(short lineNum, TEHandle hTE)
  460. {
  461.     Boolean        result = false;
  462.     short        offset, delta, lineStartOffset;
  463.     short        textLength;
  464.     Handle        textHandle;
  465.     short        pictIndex = 0;
  466.     
  467.     textHandle = (** hTE).hText;
  468.     lineStartOffset = (**hTE).lineStarts[lineNum];
  469.     textLength = (**hTE).lineStarts[lineNum+1];
  470.     
  471.     offset = 0;
  472.     while (offset < textLength)
  473.         {
  474.         if (FindNextPicture(textHandle, &offset, &delta))
  475.             {
  476.             Handle    formHandle;
  477.             
  478.             formHandle = Get1Resource(kFormResource, kFormResource + pictIndex);
  479.             if (formHandle)
  480.                 {
  481.                 long    options = **(long**)formHandle;
  482.                 
  483.                 ReleaseResource(formHandle);
  484.                 if ( (options & kFormFeed) && (offset >= lineStartOffset) && (offset < textLength) )
  485.                     {
  486.                     result = true;
  487.                     break;
  488.                     }
  489.                 }
  490.             ++pictIndex;
  491.             }
  492.         
  493.         offset += delta;
  494.         }
  495.         
  496.     return(result);
  497.     
  498. } // LineHasPageBreak
  499.  
  500. // --------------------------------------------------------------------------------------------------------------
  501. #define USE_PICT_SPOOL_CACHE 1
  502.  
  503. enum {kSpoolCacheBlockSize = 1024};        // 1K is arbitrary, perhaps could be tuned
  504.  
  505. static Handle    gSpoolPicture;
  506. static long        gSpoolOffset;
  507. static Handle    gSpoolCacheBlock;
  508. static long        gSpoolCacheOffset;
  509. static long        gSpoolCacheSize;
  510.  
  511. // --------------------------------------------------------------------------------------------------------------
  512. /*
  513.     ReadPartialResource is very painful over slow links such as ARA or ISDN. This code implements a simple
  514.     cache that vastly improves the performance of displaying embedded pictures over a slow network link.
  515.     The cache also improves scrolling performance in documents with many embedded pictures, even on local
  516.     volumes.
  517. */
  518. static short ReadPartialData(Ptr dataPtr, short byteCount)
  519. {
  520. #if USE_PICT_SPOOL_CACHE
  521.  
  522.     if (gSpoolCacheBlock == NULL)
  523.         {
  524.         gSpoolCacheBlock = NewHandle(kSpoolCacheBlockSize);
  525.         if (gSpoolCacheBlock != NULL)
  526.             {
  527.             long    cacheBytes    = GetResourceSizeOnDisk(gSpoolPicture) - gSpoolOffset;
  528.             
  529.             if (cacheBytes > kSpoolCacheBlockSize)
  530.                 cacheBytes = kSpoolCacheBlockSize;
  531.             
  532.             HLock(gSpoolCacheBlock);
  533.             ReadPartialResource(gSpoolPicture, gSpoolOffset, *gSpoolCacheBlock, cacheBytes);
  534.             HUnlock(gSpoolCacheBlock);
  535.             
  536.             gSpoolCacheOffset = gSpoolOffset;
  537.             gSpoolCacheSize = cacheBytes;
  538.             }
  539.         }
  540.     
  541.     // if the requested data is entirely present in the cache block, get it from there…
  542.     if (gSpoolCacheBlock != NULL &&
  543.         gSpoolOffset >= gSpoolCacheOffset && gSpoolOffset + byteCount <= gSpoolCacheOffset + gSpoolCacheSize)
  544.         {
  545.         BlockMoveData(*gSpoolCacheBlock + (gSpoolOffset - gSpoolCacheOffset), dataPtr, byteCount);
  546.         return byteCount;
  547.         }
  548.     // …else read it directly from disk, and force the cache block to be filled with new data
  549.     else
  550.         {
  551.         ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  552.         if (gSpoolCacheBlock != NULL)
  553.             {
  554.             DisposeHandle(gSpoolCacheBlock);
  555.             gSpoolCacheBlock = NULL;
  556.             }
  557.         return byteCount;
  558.         }
  559.  
  560. #else
  561.  
  562.     ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  563.     return byteCount;
  564.     
  565. #endif
  566.     
  567. } // ReadPartialData
  568.  
  569. // --------------------------------------------------------------------------------------------------------------
  570. static pascal void GetPartialPICTData(Ptr dataPtr,short byteCount)
  571. {
  572.     while (byteCount > 0)
  573.     {
  574.         short    readBytes    = ReadPartialData(dataPtr, byteCount);
  575.         
  576.         byteCount -= readBytes;
  577.         dataPtr += readBytes;
  578.         
  579.         gSpoolOffset += readBytes;
  580.     }
  581.     
  582. } // GetPartialPICTData
  583.  
  584. // --------------------------------------------------------------------------------------------------------------
  585. static void SpoolDrawPicture(Handle spoolPicture, PicHandle pictureHeader, Rect *pRect)
  586. {
  587.     static QDGetPicUPP gGetPartialPICTData = NULL;
  588.     CQDProcs    spoolProcs, *oldProcsPtr;
  589.     CGrafPtr    thePort = GetQDGlobalsThePort();
  590.  
  591.     if (!gGetPartialPICTData)
  592.     {
  593.          gGetPartialPICTData = NewQDGetPicUPP(GetPartialPICTData);
  594.     }
  595.  
  596.     gSpoolPicture = spoolPicture;
  597.     gSpoolOffset = sizeof(Picture);
  598.  
  599. #if 0
  600.     // 4-12-00 lhotak: this would ignore printing bottlenecks.
  601.     // Indeed, no images will ever print with this code.
  602.  
  603. //    if (!GetPortGrafProcs(thePort, &spoolProcs))
  604.         SetStdCProcs(&spoolProcs);
  605. #else
  606.     // Port's procs will be NULL if never set (window ports).
  607.     // We need to check for this case and make use of StdProcs.
  608.     // Otherwise the procs (printing ports) need to be saved
  609.     // and restored after drawing.
  610.     oldProcsPtr = GetPortGrafProcs(thePort);
  611.     if (oldProcsPtr != NULL)
  612.         spoolProcs = *oldProcsPtr;
  613.     else
  614.         SetStdCProcs(&spoolProcs);
  615. #endif
  616.  
  617.     // Use custom getPicProc for our picture drawing:
  618.     spoolProcs.getPicProc = gGetPartialPICTData;
  619.     SetPortGrafProcs(thePort, &spoolProcs);
  620.  
  621.     // Draw the picture and restore old bottlenecks:
  622.     DrawPicture(pictureHeader, pRect);
  623.     SetPortGrafProcs(thePort, oldProcsPtr);
  624.  
  625.     if (gSpoolCacheBlock != NULL)
  626.     {
  627.         DisposeHandle(gSpoolCacheBlock);
  628.         gSpoolCacheBlock = NULL;
  629.     }
  630. } // SpoolDrawPicture
  631.  
  632. /*    static QDGetPicUPP gGetPartialPICTData = NULL;
  633. #if USE_QD_GRAFPROCS
  634.     CQDProcs    spoolProcs;
  635.     CQDProcs    oldProcs;
  636.     CGrafPtr    thePort = GetQDGlobalsThePort();
  637. #endif
  638.  
  639.     if (!gGetPartialPICTData) {
  640.         gGetPartialPICTData = NewQDGetPicUPP(GetPartialPICTData);
  641.     }
  642.  
  643.     gSpoolPicture = spoolPicture;
  644.     gSpoolOffset = sizeof(Picture);
  645.     
  646. #if USE_QD_GRAFPROCS
  647.     if (!GetPortGrafProcs(thePort, &spoolProcs))
  648.         SetStdCProcs(&spoolProcs);
  649.         
  650.     spoolProcs.getPicProc = gGetPartialPICTData;
  651.     GetPortGrafProcs(thePort, &oldProcs);
  652.     SetPortGrafProcs(thePort, &spoolProcs);
  653. #endif
  654.     DrawPicture(pictureHeader, pRect);
  655. #if USE_QD_GRAFPROCS
  656.     SetPortGrafProcs(thePort, &oldProcs);
  657. #endif
  658.     
  659.     if (gSpoolCacheBlock != NULL)
  660.         {
  661.         DisposeHandle(gSpoolCacheBlock);
  662.         gSpoolCacheBlock = NULL;
  663.         }
  664.     
  665. } // SpoolDrawPicture*/
  666.  
  667. // --------------------------------------------------------------------------------------------------------------
  668. static void DrawPictures( WindowDataPtr pData, TEHandle hTE)
  669. {
  670.     Handle    textHandle;
  671.     long    textLength;
  672.     short    oldResFile;
  673.     short    pictIndex;
  674.     short    numPicts;
  675.     
  676.     if (pData->resRefNum == -1)
  677.         return;
  678.         
  679.     oldResFile = CurResFile();
  680.     UseResFile(pData->resRefNum);
  681.     
  682.     numPicts = Count1Resources('PICT');
  683.     pictIndex = 0;
  684.     
  685.     if (numPicts != 0)
  686.         {
  687.         short                offset, delta;
  688.         RgnHandle            oldClip;
  689.         Rect                theRect;
  690.         Rect                viewRect;
  691.         
  692.         viewRect = theRect = (** hTE).viewRect;
  693.         // intersect our viewing area with the actual clip to avoid
  694.         // drawing over the scroll bars
  695.             {
  696.             RgnHandle    newClip = NewRgn();
  697.             
  698.             oldClip = NewRgn();
  699.             GetClip(oldClip);
  700.             RectRgn(newClip, &theRect);
  701.             SectRgn(oldClip, newClip, newClip);
  702.             SetClip(newClip);
  703.                         DisposeRgn(newClip);
  704.             }
  705.         textHandle = (** hTE).hText;
  706.         textLength = (** hTE).teLength;
  707.         
  708.         offset = 0;
  709.         while (offset < textLength)
  710.             {
  711.             if (FindNextPicture(textHandle, &offset, &delta))
  712.                 {
  713.                 Handle        pictHandle;
  714.                 Point        picturePoint = TEGetPoint(offset, hTE);
  715.                 
  716.                 SetResLoad(false);
  717.                 pictHandle = Get1Resource('PICT', kPictureBase + pictIndex);
  718.                 SetResLoad(true);
  719.                 if (pictHandle)
  720.                     {
  721.                     PicHandle    pictHeader = (PicHandle)NewHandle(sizeof(Picture) + sizeof(long)*8);
  722.                     
  723.                     if (pictHeader)
  724.                         {
  725.                         HLock((Handle) pictHeader);
  726.                         ReadPartialResource(pictHandle, 0, (Ptr)*pictHeader, GetHandleSize((Handle)pictHeader));
  727.                         HUnlock((Handle) pictHeader);
  728.                         
  729.                         // calculate where to draw the picture, this location is
  730.                         // computed by:
  731.                         //  1) the frame of the original picture, normalized to 0,0
  732.                         //    2) the location of the non-breaking space character
  733.                         //    3) centering the picture on the content frame horizontally
  734.                         //    4) subtracting off the line-height of the character
  735.                         
  736.                         GetPICTRectangleAt72dpi(pictHeader, &theRect);
  737.                         OffsetRect(&theRect, -theRect.left, -theRect.top);
  738.                         OffsetRect(&theRect, 
  739.                                             theRect.left +
  740.                                             ((viewRect.right - viewRect.left) >> 1) -
  741.                                             ((theRect.right - theRect.left) >> 1),
  742.                                         picturePoint.v-theRect.top - pData->vScrollAmount);
  743.     
  744.         // only draw the picture if it will be visible (vastly improves scrolling
  745.         // performance in documents with many embedded pictures)
  746.                         
  747.         //    if (RectInRgn(&theRect, GetQDthePort()->clipRgn))
  748.                             SpoolDrawPicture(pictHandle, pictHeader, &theRect);
  749.                         DisposeHandle((Handle) pictHeader);
  750.                         }
  751.                     ReleaseResource((Handle) pictHandle);
  752.                     }
  753.                 ++pictIndex;
  754.  
  755.                 offset += delta;
  756.                 }
  757.             else
  758.                 break;
  759.             }
  760.                 
  761.         SetClip(oldClip);
  762.         DisposeRgn(oldClip);
  763.         }
  764.         
  765.     UseResFile(oldResFile);
  766.     
  767. } // DrawPictures
  768.  
  769. // --------------------------------------------------------------------------------------------------------------
  770. static void UpdateFileInfo( FSSpec *pSpec, Boolean isStationery )
  771. {
  772.     FInfo    theInfo;
  773.  
  774.     FSpGetFInfo(pSpec, &theInfo);
  775.     theInfo.fdCreator = 'ttxt';
  776.     theInfo.fdType = 'TEXT';
  777.  
  778.     // set the stationary bit, if we must
  779.     if ( isStationery )
  780.         theInfo.fdFlags |= kIsStationery;
  781.     else
  782.         theInfo.fdFlags &= ~kIsStationery;
  783.  
  784.     FSpSetFInfo(pSpec, &theInfo);
  785.  
  786. } // UpdateFileInfo
  787.  
  788. // --------------------------------------------------------------------------------------------------------------
  789. static OSStatus    TextSave(WindowDataPtr pData, Boolean resetStationary)
  790. {
  791.     OSStatus    anErr;
  792.     long    amountToWrite;
  793.     int        i;
  794.     Ptr        data;
  795.     
  796.     anErr = noErr;
  797.     
  798.     // write out the text
  799.     SetFPos(pData->dataRefNum, fsFromStart, 0);
  800.     amountToWrite = (** ((TextDataPtr) pData)->hTE).teLength;
  801.     data = * (** ((TextDataPtr) pData)->hTE).hText;
  802.  
  803.     for (i=0; i < amountToWrite; i++) {
  804.         if (data[i] == '\r')
  805.             data[i] = '\n';
  806.     }
  807.  
  808.     anErr = FSWrite(pData->dataRefNum, &amountToWrite, data);
  809.     amountToWrite = (** ((TextDataPtr) pData)->hTE).teLength;
  810.     data = * (** ((TextDataPtr) pData)->hTE).hText;
  811.     for (i=0; i < amountToWrite; i++) {
  812.         if (data[i] == '\n')
  813.             data[i] = '\r';
  814.     }
  815.  
  816.     nrequire(anErr, FailedWrite);
  817.  
  818.     SetEOF(pData->dataRefNum, amountToWrite);
  819.     
  820.     if (pData->resRefNum == -1)
  821.         {
  822.         FSpCreateResFile(&pData->fileSpec, 'ttxt', pData->originalFileType, 0);
  823.         pData->resRefNum = FSpOpenResFile(&pData->fileSpec, fsRdWrPerm);            
  824.         }
  825.     else
  826.         {
  827.         // a save always makes it into file of type 'TEXT', for Save As… we 
  828.         // afterwards set it again if the user is saving as stationary.
  829.         if (resetStationary)
  830.             UpdateFileInfo(&pData->fileSpec, true);
  831.         }
  832.  
  833.     if (pData->resRefNum != -1)
  834.         {
  835.         short    oldResFile = CurResFile();
  836.         Handle    resourceHandle;
  837.         
  838.         UseResFile(pData->resRefNum);
  839.         
  840.         // remove any old sounds
  841.         resourceHandle = Get1Resource('snd ', kSoundBase);
  842.         if (resourceHandle)
  843.             {
  844.             RemoveResource(resourceHandle);
  845.             DisposeHandle(resourceHandle);
  846.             }
  847.             
  848.         // save the new sound
  849.         resourceHandle = ((TextDataPtr) pData)->soundHandle;
  850.         if (resourceHandle)
  851.             {
  852.             anErr = HandToHand(&resourceHandle);
  853.             if (anErr == noErr)
  854.                 {
  855.                 AddResource(resourceHandle, 'snd ', kSoundBase, "\p");
  856.                 anErr = ResError();
  857.                 }
  858.             nrequire(anErr, AddSoundResourceFailed);
  859.             }
  860.             
  861.         // remove any old styles
  862.         resourceHandle = Get1Resource('styl', 128);
  863.         if (resourceHandle)
  864.             {
  865.             RemoveResource(resourceHandle);
  866.             DisposeHandle(resourceHandle);
  867.             }
  868.         
  869.         // save the new style -- get the scrap handle from the TE record
  870.         // To do this, we must select the text -- BUT doing so through the
  871.         // TextEdit API results in lots of flashing and annoying behavior.
  872.         // So we just change the offsets by hand
  873.         {
  874.         short                oldStart, oldEnd;    
  875.         
  876.         oldStart = (** ((TextDataPtr) pData)->hTE).selStart;
  877.         oldEnd   = (** ((TextDataPtr) pData)->hTE).selEnd;
  878.         
  879.         (** ((TextDataPtr) pData)->hTE).selStart = 0;
  880.         (** ((TextDataPtr) pData)->hTE).selEnd = 32767;
  881.     
  882.         resourceHandle = (Handle) TEGetStyleScrapHandle( ((TextDataPtr) pData)->hTE );
  883.     
  884.         (** ((TextDataPtr) pData)->hTE).selStart = oldStart;
  885.         (** ((TextDataPtr) pData)->hTE).selEnd = oldEnd;
  886.         }
  887.     
  888.         if (resourceHandle)
  889.             {
  890.             AddResource(resourceHandle, 'styl', 128, "\p");
  891.             anErr = ResError();
  892.             nrequire(anErr, AddStyleResourceFailed);
  893.             }
  894.             
  895.         AddSoundResourceFailed:
  896.         AddStyleResourceFailed:    
  897.         
  898.             UpdateResFile(pData->resRefNum);
  899.             UseResFile(oldResFile);
  900.         }
  901.  
  902.         
  903. // FALL THROUGH EXCEPTION HANDLING
  904. FailedWrite:
  905.  
  906.     // if everything went okay, then clear the changed bit
  907.     if (anErr == noErr)
  908.     {
  909.         (void) FlushVol("\p", pData->fileSpec.vRefNum);
  910.  
  911.         // we need not update the proxy here, since the file type never changes in this routine
  912.         
  913.         SetDocumentContentChanged( pData, false );
  914.     }
  915.         
  916.     return anErr;
  917.     
  918. } // TextSave
  919.  
  920. // --------------------------------------------------------------------------------------------------------------
  921. #if 0
  922. static pascal void DrawTextUserItem(DialogPtr dPtr, short theItem)
  923. /*
  924.     Draw text icon in the location
  925. */
  926. {
  927.     short        kind;
  928.     Handle        itemHandle;
  929.     Rect        box;
  930.     
  931.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  932.     PlotIconID(&box, ttNone, ttNone, kTextIcon);
  933.             
  934. } // DrawTextUserItem
  935.  
  936. #if GENERATINGCFM
  937.     static RoutineDescriptor gDrawTextUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawTextUserItem);
  938.     static UserItemUPP gDrawTextUserItem = &gDrawTextUserItemRD;
  939. #else
  940.     static UserItemUPP gDrawTextUserItem = NewUserItemProc(DrawTextUserItem);
  941. #endif
  942. #endif
  943.  
  944. // --------------------------------------------------------------------------------------------------------------
  945. #if 0
  946. static pascal void DrawStationeryUserItem(DialogPtr dPtr, short theItem)
  947. /*
  948.     Draw stationery icon in the location
  949. */
  950. {
  951.     short        kind;
  952.     Handle        itemHandle;
  953.     Rect        box;
  954.     
  955.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  956.     PlotIconID(&box, ttNone, ttNone, kStationeryIcon);
  957.             
  958. } // DrawStationeryUserItem
  959.  
  960.  
  961. #if GENERATINGCFM
  962.     static RoutineDescriptor gDrawStationeryUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawStationeryUserItem);
  963.     static UserItemUPP gDrawStationeryUserItem = &gDrawStationeryUserItemRD;
  964. #else
  965.     static UserItemUPP gDrawStationeryUserItem = NewUserItemProc(DrawStationeryUserItem);
  966. #endif
  967. #endif
  968.  
  969. // --------------------------------------------------------------------------------------------------------------
  970. // pre and post update procs for inline input 
  971.  
  972. static pascal void TSMPreUpdateProc(TEHandle textH, long refCon)
  973. {
  974. #pragma unused (refCon)
  975.  
  976.     ScriptCode     keyboardScript;
  977.     short        mode;
  978.     TextStyle    theStyle;
  979.  
  980.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  981.     mode = doFont;
  982.     if     (!
  983.             (
  984.             (TEContinuousStyle(&mode, &theStyle, textH) )&& 
  985.             (FontToScript(theStyle.tsFont) == keyboardScript) 
  986.             )
  987.         )
  988.         {
  989.         theStyle.tsFont = GetScriptVariable(keyboardScript, smScriptAppFond);
  990.         TESetStyle(doFont, &theStyle, false, textH);
  991.         }
  992.         
  993. } // TSMPreUpdateProc
  994.  
  995. static pascal void TSMPostUpdateProc(
  996.                 TEHandle textH,
  997.                 long fixLen,
  998.                 long inputAreaStart,
  999.                 long inputAreaEnd,
  1000.                 long pinStart,
  1001.                 long pinEnd,
  1002.                 long refCon)
  1003. {
  1004. #pragma unused (textH, fixLen, inputAreaStart, inputAreaEnd, pinStart, pinEnd)
  1005.  
  1006.     AdjustScrollBars((WindowPtr)refCon, false, false, nil);
  1007.     AdjustTE((WindowDataPtr)refCon, true);
  1008.     
  1009.     SetDocumentContentChanged( (WindowDataPtr)refCon, true );
  1010.     
  1011. } // TSMPostUpdateProc
  1012.  
  1013.  
  1014. // --------------------------------------------------------------------------------------------------------------
  1015.  
  1016. static OSStatus    TextSaveAs( WindowPtr pWindow, WindowDataPtr pData )
  1017. {    
  1018.     // ask where and how to save this document
  1019.  
  1020.     Str255    defaultName;
  1021.     Cursor    arrow;
  1022.  
  1023.     // setup for the call
  1024.     GetWTitle(pWindow, defaultName);
  1025.  
  1026.     SetCursor(GetQDGlobalsArrow(&arrow));
  1027.     
  1028.     return SaveFileDialog( pWindow, defaultName, pData->originalFileType, 'ttxt', pData, &pData->navDialog );
  1029. } // TextSaveAs
  1030.  
  1031.  
  1032. // --------------------------------------------------------------------------------------------------------------
  1033. static void ApplyFace(short requestedFace, WindowPtr pWindow, WindowDataPtr pData, short commandID)
  1034. {
  1035.     TextStyle    style;
  1036.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1037.     
  1038.     SaveCurrentUndoState(pData, commandID);
  1039.     
  1040.     style.tsFace = requestedFace;
  1041.     TESetStyle(doFace + ((requestedFace != normal) ? doToggle : 0), &style, true, ((TextDataPtr) pData)->hTE);
  1042.     TECalText(((TextDataPtr) pData)->hTE);
  1043.     
  1044.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1045.     AdjustTE(pData, false);
  1046.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1047.  
  1048.     SetDocumentContentChanged( (WindowDataPtr) pData, true );    
  1049. } // ApplyFace
  1050.  
  1051. // --------------------------------------------------------------------------------------------------------------
  1052. static OSStatus ApplySize(short requestedSize, WindowPtr pWindow, WindowDataPtr pData, short commandID)
  1053. {
  1054.     OSStatus        anErr = noErr;
  1055.     TextStyle    style;
  1056.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1057.     
  1058.     SaveCurrentUndoState(pData, commandID);
  1059.  
  1060.     style.tsSize = requestedSize;
  1061.     TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1062.     TECalText(((TextDataPtr) pData)->hTE);
  1063.     if (CalculateTextEditHeight(((TextDataPtr) pData)->hTE) > 32767)
  1064.         {        
  1065.         style.tsSize = 0;
  1066.         TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1067.         anErr = eDocumentTooLarge;
  1068.         }
  1069.  
  1070.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1071.     AdjustTE(pData, false);
  1072.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1073.  
  1074.     SetDocumentContentChanged( (WindowDataPtr) pData, true );
  1075.     
  1076.     return anErr;
  1077.  
  1078. } // ApplySize
  1079.  
  1080. // --------------------------------------------------------------------------------------------------------------
  1081. // OOP INTERFACE ROUTINES
  1082. // --------------------------------------------------------------------------------------------------------------
  1083.  
  1084. static OSStatus    TextUpdateWindow(WindowPtr pWindow, WindowDataPtr pData)
  1085. {
  1086.     Rect    updateArea = pData->contentRect;
  1087.     Rect    bounds;
  1088.  
  1089.     // be sure to also erase the area where the horizontal scroll bar
  1090.     // is missing.
  1091.     updateArea.bottom = GetWindowPortBounds(pWindow, &bounds)->bottom;
  1092.     EraseRect(&updateArea);
  1093.     
  1094.     TEUpdate(&pData->contentRect, ((TextDataPtr) pData)->hTE);
  1095.  
  1096.     DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1097.     
  1098.     DrawControls(pWindow);
  1099.     DrawGrowIcon(pWindow);
  1100.     
  1101.     return noErr;    
  1102. } // TextUpdateWindow
  1103.  
  1104. // --------------------------------------------------------------------------------------------------------------
  1105.  
  1106. static OSStatus    TextCloseWindow(WindowPtr pWindow, WindowDataPtr pData)
  1107. {
  1108. #pragma unused (pWindow)
  1109.  
  1110.     // shut down text services
  1111.     if (pData->docTSMDoc)
  1112.         {
  1113.         FixTSMDocument(pData->docTSMDoc);
  1114.         DeactivateTSMDocument(pData->docTSMDoc);
  1115.         DeleteTSMDocument(pData->docTSMDoc);
  1116.         }
  1117.     DisposeOfSpeech(true);
  1118.  
  1119.     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1120.     TEDispose(((TextDataPtr) pData)->hTE);
  1121.  
  1122.     DisposeHandle(((TextDataPtr) pData)->prevText);
  1123.     DisposeHandle(((TextDataPtr) pData)->prevStyle);
  1124.     
  1125.     TextRemoveContentsMenu(pData);
  1126.  
  1127.     return noErr;
  1128.  
  1129. } // TextCloseWindow
  1130.  
  1131. // --------------------------------------------------------------------------------------------------------------
  1132.  
  1133. static OSStatus TextSaveTo( WindowPtr pWindow, WindowDataPtr pData, FSSpec *fileSpec, Boolean isStationery )
  1134. {
  1135.     OSStatus anErr = noErr;
  1136.         
  1137.     //
  1138.     // Same file?  If so, just save.
  1139.     //
  1140.     if     (( pData->fileSpec.vRefNum == fileSpec->vRefNum )
  1141.             && ( pData->fileSpec.parID == fileSpec->parID ) 
  1142.             && EqualString( pData->fileSpec.name, fileSpec->name, false, false ))
  1143.     {
  1144.  
  1145.         anErr = TextSave(pData, true);
  1146.         
  1147.         if ( anErr == noErr )
  1148.         {
  1149.             UpdateFileInfo( fileSpec, isStationery );
  1150.         }
  1151.     }
  1152.     else
  1153.     {
  1154.         short                oldRes, oldData;
  1155.         FSSpec                oldFileSpec;
  1156.         
  1157.         // save the old references -- if there is an error, we restore them
  1158.         oldRes                = pData->resRefNum;
  1159.         oldData                = pData->dataRefNum;
  1160.         BlockMoveData( &pData->fileSpec, &oldFileSpec, sizeof( FSSpec ));
  1161.  
  1162.         // create the data file and resource fork
  1163.         //(void) FSpDelete(&fileSpec);
  1164.         //anErr = FSpCreate(&fileSpec, 'ttxt', 'TEXT', 0);
  1165.         FSpCreateResFile( fileSpec, 'ttxt', 'TEXT', 0 );
  1166.         require( anErr == noErr || anErr == dupFNErr, FailedCreate);
  1167.  
  1168.         UpdateFileInfo( fileSpec, isStationery );
  1169.  
  1170.         // open both of forks
  1171.         anErr = FSpOpenDF( fileSpec, fsRdWrPerm, &pData->dataRefNum );
  1172.         if ( anErr == noErr )
  1173.         {
  1174.             pData->resRefNum = FSpOpenResFile( fileSpec, fsRdWrPerm );
  1175.             anErr = ResError();
  1176.         }
  1177.         nrequire(anErr, FailedOpen);
  1178.  
  1179.         BlockMoveData( fileSpec, &pData->fileSpec, sizeof( FSSpec ));
  1180.         // call the standard save function to do the save
  1181.         anErr = TextSave(pData, false);
  1182.  
  1183.     FailedOpen:
  1184.  
  1185.         // finally, close the old files if everything went okay
  1186.         if ( anErr == noErr )
  1187.         {
  1188.             if (oldRes != -1)
  1189.                 CloseResFile(oldRes);
  1190.             if (oldData != -1)
  1191.                 FSClose(oldData);
  1192.             pData->isWritable = true;
  1193.             SetWTitle(pWindow, fileSpec->name);
  1194.             
  1195.             // update the window icon
  1196.             if( gMachineInfo.haveProxyIcons )
  1197.                 SetWindowProxyFSSpec( pWindow, fileSpec );
  1198.         }
  1199.         else
  1200.         {
  1201.             pData->resRefNum = oldRes;
  1202.             pData->dataRefNum = oldData;
  1203.             BlockMoveData( &oldFileSpec, &pData->fileSpec, sizeof( FSSpec ));
  1204.         }
  1205.         
  1206.     }
  1207.     
  1208.     FailedCreate:
  1209.  
  1210.     // file must be closed in order to 'translate in place', so close both forks:
  1211.     if ( pData->resRefNum != -1 )
  1212.     {
  1213.         CloseResFile( pData->resRefNum );
  1214.         pData->resRefNum = -1;
  1215.     }
  1216.     if ( pData->dataRefNum != -1 )
  1217.     {
  1218.         FSClose( pData->dataRefNum );
  1219.         pData->dataRefNum = -1;
  1220.     }
  1221.  
  1222.         // •• problem:
  1223.         // Save As… model fails in this case because we still have the old window open,
  1224.         // Should SimpleText close the current text window and open a new one with the translated data?
  1225.  
  1226.         // ANSWER: keep the reply record and call NavCompleteSave every time you save the
  1227.         // file from now on. This will translate every time.  (CSL)
  1228.  
  1229.     return anErr;
  1230.     
  1231. } // TextSaveTo
  1232.  
  1233. // --------------------------------------------------------------------------------------------------------------
  1234.  
  1235. static OSStatus    TextActivateEvent(WindowPtr pWindow, WindowDataPtr pData, Boolean activating)
  1236. {
  1237. #pragma unused (pWindow)
  1238.  
  1239.     // only modifyable docs can be active
  1240.     if (pData->originalFileType == 'TEXT')
  1241.         {
  1242.         if (activating)
  1243.             {
  1244.             TEActivate(((TextDataPtr) pData)->hTE);
  1245.             if (pData->docTSMDoc != nil)
  1246.                 ActivateTSMDocument(pData->docTSMDoc);
  1247.             }
  1248.         else
  1249.             {
  1250.             TEDeactivate(((TextDataPtr) pData)->hTE);
  1251.             if (pData->docTSMDoc != nil)
  1252.                 DeactivateTSMDocument(pData->docTSMDoc);
  1253.             }
  1254.         }
  1255.         
  1256.     // add contents menu, if appropriate        
  1257.     if (activating)
  1258.         {
  1259.         TextAddContentsMenu(pData);
  1260.         }
  1261.     else
  1262.         {
  1263.         TextRemoveContentsMenu(pData);
  1264.         }
  1265.     
  1266.     return noErr;
  1267.     
  1268. } // TextActivateEvent
  1269.  
  1270. // --------------------------------------------------------------------------------------------------------------
  1271.  
  1272. static Boolean    TextFilterEvent(WindowPtr pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1273. {    
  1274.     switch (pEvent->what)
  1275.         {
  1276.         case nullEvent:
  1277.             if (pData->originalFileType == 'TEXT')
  1278.                 {
  1279.                 if ( pWindow == FrontWindow() )
  1280.                     TEIdle(((TextDataPtr) pData)->hTE);
  1281.                 }
  1282.             
  1283.             // if we stop speaking, ditch the channel
  1284.             if (gSpeechChannel) 
  1285.                 {
  1286.                 SpeechStatusInfo    status;                // Status of our speech channel.
  1287.                 
  1288.                 if ( 
  1289.                     (GetSpeechInfo( gSpeechChannel, soStatus, (void*) &status ) == noErr)
  1290.                     &&  (!status.outputBusy )
  1291.                     )
  1292.                     DisposeOfSpeech(true);
  1293.                 }
  1294.             break;
  1295.         }
  1296.         
  1297.     return false;
  1298.     
  1299. } // TextFilterEvent
  1300.  
  1301. // --------------------------------------------------------------------------------------------------------------
  1302.  
  1303. static OSStatus    TextScrollContent(WindowPtr pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  1304. {
  1305.     CGrafPtr    port = GetWindowPort(pWindow);
  1306.     RgnHandle    srcRgn, dstRgn, visRgn, clipRgn;
  1307.     Rect        viewRect;
  1308.     
  1309.     // scroll the text area
  1310.     TEScroll(deltaH, deltaV, ((TextDataPtr) pData)->hTE);
  1311.  
  1312.     // calculate the region that is uncovered by the scroll
  1313.     srcRgn = NewRgn();
  1314.         dstRgn = NewRgn();
  1315.         visRgn = NewRgn();
  1316.         clipRgn = NewRgn();
  1317.         GetPortVisibleRegion(port, visRgn);
  1318.         GetPortClipRegion(port, clipRgn);
  1319.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  1320.     RectRgn( srcRgn, &viewRect );
  1321.     SectRgn( srcRgn, visRgn,  srcRgn );
  1322.     SectRgn( srcRgn, clipRgn, srcRgn );
  1323.     CopyRgn( srcRgn, dstRgn );
  1324.     OffsetRgn( dstRgn, deltaH, deltaV );
  1325.     SectRgn( srcRgn, dstRgn, dstRgn );
  1326.     DiffRgn( srcRgn, dstRgn, srcRgn );
  1327.  
  1328.     // clip to this new area
  1329.     GetClip(dstRgn);
  1330.     SetClip(srcRgn);
  1331.         DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1332.     SetClip(dstRgn);
  1333.     
  1334.     // all done with these calculation regions
  1335.     DisposeRgn( srcRgn );
  1336.         DisposeRgn( dstRgn );
  1337.         DisposeRgn( visRgn );
  1338.         DisposeRgn( clipRgn );
  1339.     
  1340.     return eActionAlreadyHandled;
  1341.     
  1342. } // TextScrollContent
  1343.  
  1344. // --------------------------------------------------------------------------------------------------------------
  1345.  
  1346. static OSStatus    TextKeyEvent(WindowPtr pWindow, WindowDataPtr pData, EventRecord *pEvent, Boolean isMotionKey)
  1347. {    
  1348.     OSStatus    anErr = noErr;
  1349.     
  1350.     if (!(pEvent->modifiers & cmdKey)) 
  1351.         {
  1352.         char    theKey = pEvent->message & charCodeMask;
  1353.         char    theKeyCode = (pEvent->message >> 8) & charCodeMask;
  1354.         
  1355.         if ( 
  1356.             ((** ((TextDataPtr) pData)->hTE).teLength+1 > kMaxLength) &&
  1357.                 (    
  1358.                 (theKey != kDeleteKey) &&
  1359.                 (theKeyCode != kForwardDeleteKey) &&
  1360.                 (theKeyCode != kUpArrow) &&
  1361.                 (theKeyCode != kDownArrow) &&
  1362.                 (theKeyCode != kRightArrow) &&
  1363.                 (theKeyCode != kLeftArrow)
  1364.                 ) 
  1365.             )
  1366.             anErr = eDocumentTooLarge;
  1367.         else
  1368.             {
  1369.             long        oldHeight = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1370.             long        end = (**(((TextDataPtr) pData)->hTE)).selEnd;
  1371.             long        start = (**(((TextDataPtr) pData)->hTE)).selStart;
  1372.  
  1373.             ObscureCursor();
  1374.             SaveCurrentUndoState(pData, cTypingCommand);
  1375.             if (theKeyCode != kForwardDeleteKey)
  1376.                 {
  1377.                 if (pEvent->modifiers & shiftKey)
  1378.                     {
  1379.                     switch (theKeyCode)
  1380.                         {
  1381.                         case kUpArrow:
  1382.                         case kLeftArrow:
  1383.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1384.                             TESetSelect((**(((TextDataPtr) pData)->hTE)).selStart, end, ((TextDataPtr) pData)->hTE);
  1385.                             break;
  1386.                             
  1387.                         case kDownArrow:
  1388.                         case kRightArrow:
  1389.                             TESetSelect(end, end, ((TextDataPtr) pData)->hTE);
  1390.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1391.                             TESetSelect(start, (**(((TextDataPtr) pData)->hTE)).selEnd, ((TextDataPtr) pData)->hTE);
  1392.                             break;
  1393.                         
  1394.                         default:
  1395.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1396.                         }
  1397.                     }
  1398.                 else
  1399.                     TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1400.                 }
  1401.             else
  1402.                 {
  1403.                 if (gTE6Version)
  1404.                     {
  1405.                     TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1406.                     }
  1407.                 else
  1408.                     {
  1409.                     if     ( end < (**(((TextDataPtr) pData)->hTE)).teLength )
  1410.                         {
  1411.                         if (start == end)
  1412.                             TEKey(kRightArrowCharCode, ((TextDataPtr) pData)->hTE);
  1413.                         TEKey(kBackspaceCharCode, ((TextDataPtr) pData)->hTE);
  1414.                         }
  1415.                     }
  1416.                 }
  1417.             oldHeight -= CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1418.             
  1419.             // AEC, changed pWindow to pData for 'TextDataPtr' casts
  1420.             ((TextDataPtr) pData)->insideClickLoop = true;
  1421.                 AdjustTE(pData, false);
  1422.                 AdjustScrollBars(pWindow, (oldHeight > 0), (oldHeight > 0), nil);
  1423.             ((TextDataPtr) pData)->insideClickLoop = false;
  1424.  
  1425.             if (!isMotionKey)
  1426.                 {
  1427.                 SetDocumentContentChanged( pData, true );
  1428.                 }
  1429.             
  1430.             }
  1431.         }
  1432.  
  1433.     return anErr;
  1434.  
  1435. } // TextKeyEvent
  1436.  
  1437. // --------------------------------------------------------------------------------------------------------------
  1438.  
  1439. static OSStatus    TextContentClick(WindowPtr pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1440. {
  1441.     OSStatus            anErr = noErr;
  1442.     Point            clickPoint = pEvent->where;
  1443.     ControlHandle    theControl;
  1444.     RgnHandle        hilightRgn;
  1445.  
  1446.     GlobalToLocal(&clickPoint);
  1447.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  1448.         {
  1449.         if (gMachineInfo.haveDragMgr)
  1450.             {
  1451.             hilightRgn = NewRgn();
  1452.             TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  1453.  
  1454.             if (PtInRgn(clickPoint, hilightRgn))
  1455.                 {
  1456.                 SaveCurrentUndoState(pData, cTypingCommand);
  1457.                 if (!DragText(pWindow, pData, pEvent, hilightRgn))
  1458.                     anErr = eActionAlreadyHandled;
  1459.                 }
  1460.             else        
  1461.                 {
  1462.                 anErr = eActionAlreadyHandled;
  1463.                 }
  1464.     
  1465.             DisposeRgn(hilightRgn);
  1466.             }
  1467.         else
  1468.             {
  1469.             anErr = eActionAlreadyHandled;
  1470.             }
  1471.         }
  1472.  
  1473.     if ( (anErr == eActionAlreadyHandled) && (PtInRect(clickPoint, &pData->contentRect)) )
  1474.         {
  1475.         TEClick(clickPoint, (pEvent->modifiers & shiftKey) != 0, ((TextDataPtr) pData)->hTE);
  1476.         }
  1477.         
  1478.     return anErr;
  1479.     
  1480. } // TextContentClick
  1481.  
  1482. // --------------------------------------------------------------------------------------------------------------
  1483.  
  1484. static OSStatus    TextAdjustSize(WindowPtr pWindow, WindowDataPtr pData, 
  1485.             Boolean *didReSize) // input: was window resized, output: t->we resized something
  1486. {
  1487. #pragma unused (pWindow)
  1488.     
  1489.     if (*didReSize)
  1490.         {
  1491.         RecalcTE(pData, true);
  1492.         AdjustTE(pData, true);
  1493.         }
  1494.     else
  1495.         {
  1496.         AdjustTE(pData, false);
  1497.         }
  1498.         
  1499.     return noErr;
  1500.     
  1501. } // TextAdjustSize
  1502.  
  1503. static OSStatus    TextCommand(WindowPtr pWindow, WindowDataPtr pData, short commandID, long menuResult)
  1504. {
  1505.  
  1506.     OSStatus    anErr = noErr;
  1507.     AEDesc     theSourceText = {typeNull, NULL};            // for AppleScript
  1508.     AEDesc     theResultText = {typeNull, NULL};            // for AppleScript
  1509.     OSAID     resultID;                        // for AppleScript
  1510.     Ptr     resultTextPtr;                        // for AppleScript
  1511.     Size     resultDataSize;                        // for AppleScript
  1512.  
  1513.     SetPortWindowPort (pWindow);
  1514.  
  1515.     if ( (pData->docTSMDoc) && (menuResult != 0) )
  1516.         FixTSMDocument(pData->docTSMDoc);
  1517.         
  1518.     switch (commandID)
  1519.         {            
  1520.         case cUndo:
  1521.             {
  1522.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1523.  
  1524.             PerformUndo(pData);
  1525.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1526.             AdjustTE(pData, false);
  1527.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1528.             RecalcTE(pData, true);
  1529.             
  1530.             SetDocumentContentChanged( pData, true );
  1531.                 
  1532.             }
  1533.             break;
  1534.             
  1535.         case cCut:
  1536.             SaveCurrentUndoState(pData, cCut);
  1537.             {
  1538.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1539.  
  1540.             TECut(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1541.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1542.             AdjustTE(pData, false);
  1543.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1544.         
  1545.             SetDocumentContentChanged( (WindowDataPtr) pData, true );
  1546.                 
  1547.             }
  1548.             break;
  1549.             
  1550.         case cCopy:
  1551.             TECopy(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1552.             AdjustTE(pData, false);
  1553.             break;
  1554.             
  1555.         case cClear:
  1556.             SaveCurrentUndoState(pData, cClear);
  1557.             {
  1558.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1559.  
  1560.             TEDelete(((TextDataPtr) pData)->hTE);
  1561.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1562.             AdjustTE(pData, false);
  1563.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1564.             
  1565.             SetDocumentContentChanged( pData, true );
  1566.                 
  1567.             }
  1568.             break;
  1569.             
  1570.         case cPaste:
  1571.             SaveCurrentUndoState(pData, cPaste);
  1572.             {
  1573.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1574.  
  1575.             anErr = TEFromScrap();            
  1576.             if (anErr == noErr)
  1577.                 {                
  1578.                 // if the current length, plus the paste data, minus the data in the selection
  1579.                 // would make the document too large, say so
  1580.                 if     ( 
  1581.                         ((** ((TextDataPtr) pData)->hTE).teLength +
  1582.                         TEGetScrapLength() -
  1583.                         ((** ((TextDataPtr) pData)->hTE).selEnd-(** ((TextDataPtr) pData)->hTE).selStart)
  1584.                         )
  1585.                     > kMaxLength)
  1586.                     {
  1587.                     anErr = eDocumentTooLarge;
  1588.                     }
  1589.                 else
  1590.                     {
  1591.                     Handle    aHandle = (Handle) TEGetText(((TextDataPtr) pData)->hTE);
  1592.                     Size    oldSize = GetHandleSize(aHandle);
  1593.                     Size    newSize = oldSize + TEGetScrapLength();
  1594.                     OSStatus    saveErr;
  1595.                     
  1596.                     SetHandleSize(aHandle, newSize);
  1597.                     saveErr = MemError();
  1598.                     SetHandleSize(aHandle, oldSize);
  1599.                     if (saveErr != noErr)
  1600.                         anErr = eDocumentTooLarge;
  1601.                     else
  1602.                         TEStylePaste(((TextDataPtr) pData)->hTE);
  1603.                     }
  1604.                 }
  1605.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1606.             AdjustTE(pData, false);
  1607.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1608.             
  1609.             SetDocumentContentChanged( pData, true );
  1610.             }
  1611.             break;
  1612. #if !SIMPLER_TEXT            
  1613.         case cReplace:
  1614.             SaveCurrentUndoState(pData, cReplace);
  1615.             {
  1616.             short result = ConductFindOrReplaceDialog(kReplaceWindowID);
  1617.             
  1618.             if (result == cancel)
  1619.                 break;
  1620.                 
  1621.             if (result == iReplaceAll)
  1622.                 {
  1623.                 long         newStart, newEnd;
  1624.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1625.                 
  1626.                 TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  1627.                 while (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1628.                                     (**((TextDataPtr) pData)->hTE).selStart,
  1629.                                     gFindString, gCaseSensitive, false, false,
  1630.                                     &newStart, &newEnd))
  1631.                     {
  1632.                     TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1633.                     TEDelete(((TextDataPtr) pData)->hTE);
  1634.                     TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1635.                     TESetSelect(newStart+gReplaceString[0], newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1636.  
  1637.                     SetDocumentContentChanged( (WindowDataPtr) pData, true );    
  1638.                     }
  1639.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1640.                 
  1641.                 AdjustTE(pData, false);
  1642.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1643.     
  1644.                 anErr = eActionAlreadyHandled;
  1645.                 break;
  1646.                 }
  1647.             }
  1648.         
  1649.         // fall through from replace
  1650.         case cReplaceAgain:
  1651.             SaveCurrentUndoState(pData, cReplaceAgain);
  1652.             {
  1653.             long     newStart, newEnd;
  1654.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  1655.             
  1656.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1657.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selEnd : (**((TextDataPtr) pData)->hTE).selStart,
  1658.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  1659.                                 &newStart, &newEnd))
  1660.                 {
  1661.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1662.                 
  1663.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1664.                 TEDelete(((TextDataPtr) pData)->hTE);
  1665.                 TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1666.                 TESetSelect(newStart+gReplaceString[0], newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1667.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1668.                 
  1669.                 AdjustTE(pData, false);
  1670.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1671.  
  1672.                 SetDocumentContentChanged( pData, true );
  1673.                 }
  1674.             else
  1675.                 SysBeep(1);
  1676.  
  1677.             anErr = eActionAlreadyHandled;
  1678.             }
  1679.             break;
  1680. #endif /* !SIMPLER_TEXT */
  1681.             
  1682.         // for AppleScript
  1683.         case cExecute:
  1684.             {
  1685.             //fprintf(stderr, "Attempting to execute and applescript selection.\n");
  1686.             SaveCurrentUndoState(pData, cExecute);
  1687.             AECreateDesc(typeChar, (*(**((TextDataPtr) pData)->hTE).hText) + (**((TextDataPtr) pData)->hTE).selStart, 
  1688.                          (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart,
  1689.                          &theSourceText);
  1690.             anErr = OSACompileExecute( gOSAComponent, &theSourceText, kOSANullScript, 
  1691.                          kAECanInteract, &resultID);
  1692.             if (anErr == noErr)
  1693.             {
  1694.                 anErr = OSADisplay( gOSAComponent, resultID, typeChar, kOSAModeNull, &theResultText);
  1695.                 if (anErr == noErr) 
  1696.                 {
  1697.                     TESetSelect((**((TextDataPtr) pData)->hTE).selEnd, (**((TextDataPtr) pData)->hTE).selEnd, 
  1698.                               ((TextDataPtr) pData)->hTE);
  1699.                     resultTextPtr = NewPtr(resultDataSize = AEGetDescDataSize(&theResultText));
  1700.                     AEGetDescData(&theResultText, resultTextPtr, resultDataSize);
  1701.                     TEInsert(" --> ", 5, ((TextDataPtr) pData)->hTE);
  1702.                     TEInsert(resultTextPtr, resultDataSize, ((TextDataPtr) pData)->hTE);
  1703.                     AdjustTE(pData, false);
  1704.                     DisposePtr(resultTextPtr);
  1705.                     AEDisposeDesc(&theResultText);
  1706.                 }
  1707.                 //else fprintf(stderr, "OSADisplay returned error %i.\n", anErr);
  1708.             }
  1709.             //else fprintf(stderr, "OSACompileExecute returned error %i.\n", anErr);
  1710.  
  1711.             anErr = OSADispose( gOSAComponent, resultID);
  1712.             AEDisposeDesc(&theSourceText);
  1713.             SetDocumentContentChanged( pData, true);
  1714.             break;
  1715.             }
  1716.  
  1717. #if !SIMPLER_TEXT
  1718.         case cFind:
  1719.         case cFindSelection:
  1720.             if (commandID == cFind)
  1721.                 {
  1722.                 if (ConductFindOrReplaceDialog(kFindWindowID) == cancel)    
  1723.                     break;
  1724.                 }
  1725.             else
  1726.                 {
  1727.                 gFindString[0] = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  1728.                 BlockMoveData( (*(**((TextDataPtr) pData)->hTE).hText) + (**((TextDataPtr) pData)->hTE).selStart, &gFindString[1], gFindString[0]);
  1729.                 }
  1730.             
  1731.         // fall through from find or find selection
  1732.         case cFindAgain:
  1733.             {
  1734.             long     newStart, newEnd;
  1735.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  1736.             
  1737.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1738.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selStart : (**((TextDataPtr) pData)->hTE).selEnd,
  1739.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  1740.                                 &newStart, &newEnd))
  1741.                 {
  1742.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1743.                 AdjustTE(pData, false);
  1744.                 AdjustScrollBars(pWindow, false, false, nil);
  1745.                 }
  1746.             else
  1747.                 SysBeep(1);
  1748.  
  1749.             anErr = eActionAlreadyHandled;
  1750.             }
  1751.             break;
  1752. #endif
  1753.             
  1754.         case cSelectAll:
  1755.             TESetSelect(0, (**((TextDataPtr) pData)->hTE).teLength, 
  1756.                         ((TextDataPtr) pData)->hTE);
  1757.             AdjustTE(pData, false);
  1758.             AdjustScrollBars(pWindow, false, false, nil);
  1759.             DoAdjustCursor(pWindow, nil);
  1760.             anErr = eActionAlreadyHandled;
  1761.             break;
  1762.         
  1763.         // save turns into save as if this is a new document or if the original wasn't
  1764.         // available for writing
  1765.         case cSave:
  1766.             if     (
  1767.                 (!pData->isWritable) || 
  1768.                 ( (pData->dataRefNum == -1) && (pData->resRefNum == -1) )
  1769.                 )
  1770.                 anErr = TextSaveAs(pWindow, pData);
  1771.             else
  1772.                 anErr = TextSave(pData, true);
  1773.             break;
  1774.             
  1775.         case cSaveAs:
  1776.             anErr = TextSaveAs(pWindow, pData);
  1777.             break;
  1778.             
  1779.         // SUPPORTED FONTS
  1780.         case cSelectFont:
  1781.             SaveCurrentUndoState(pData, cSelectFont);
  1782.             {
  1783.             Str255        itemName;
  1784.             TextStyle    theStyle;
  1785.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1786.             
  1787.             GetMenuItemText( GetMenuHandle( menuResult>>16 ), menuResult & 0xFFFF, itemName );
  1788.             GetFNum( itemName, &theStyle.tsFont );
  1789.             TESetStyle( doFont, &theStyle, true, ((TextDataPtr) pData)->hTE );
  1790.             TECalText(((TextDataPtr) pData)->hTE);
  1791.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1792.             AdjustTE(pData, false);
  1793.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1794.             
  1795.             SetDocumentContentChanged( pData, true );
  1796.             }
  1797.             break;
  1798.             
  1799.             
  1800.         // SUPPORTED STYLES
  1801.         case cPlain:
  1802.             ApplyFace(normal, pWindow, pData, cPlain);
  1803.             break;
  1804.             
  1805.         case cBold:
  1806.             ApplyFace(bold, pWindow, pData, cBold);
  1807.             break;
  1808.  
  1809.         case cItalic:
  1810.             ApplyFace(italic, pWindow, pData, cItalic);
  1811.             break;
  1812.             
  1813.         case cUnderline:
  1814.             ApplyFace(underline, pWindow, pData, cUnderline);
  1815.             break;
  1816.             
  1817.         case cOutline:
  1818.             ApplyFace(outline, pWindow, pData, cOutline);
  1819.             break;
  1820.             
  1821.         case cShadow:
  1822.             ApplyFace(shadow, pWindow, pData, cShadow);
  1823.             break;
  1824.  
  1825.         case cCondensed:
  1826.             ApplyFace(condense, pWindow, pData, cCondensed);
  1827.             break;
  1828.             
  1829.         case cExtended:
  1830.             ApplyFace(extend, pWindow, pData, cExtended);
  1831.             break;
  1832.             
  1833.     
  1834.         // SUPPORTED SIZES
  1835.         case cSize9:
  1836.             anErr = ApplySize(9, pWindow, pData, cSize9);
  1837.             break;
  1838.             
  1839.         case cSize10:
  1840.             anErr = ApplySize(10, pWindow, pData, cSize10);
  1841.             break;
  1842.             
  1843.         case cSize12:
  1844.             anErr = ApplySize(12, pWindow, pData, cSize12);
  1845.             break;
  1846.             
  1847.         case cSize14:
  1848.             anErr = ApplySize(14, pWindow, pData, cSize14);
  1849.             break;
  1850.             
  1851.         case cSize18:
  1852.             anErr = ApplySize(18, pWindow, pData, cSize18);
  1853.             break;
  1854.             
  1855.         case cSize24:
  1856.             anErr = ApplySize(24, pWindow, pData, cSize24);
  1857.             break;
  1858.             
  1859.         case cSize36:
  1860.             anErr = ApplySize(36, pWindow, pData, cSize36);
  1861.             break;
  1862.             
  1863. #if 0            
  1864.         // SUPPORTED SOUND COMMANDS
  1865.         case cRecord:
  1866.             {
  1867.             Handle    tempHandle;
  1868.  
  1869.             // allocate our prefered buffer if we can, but if we can't
  1870.             // make sure that at least a minimum amount of RAM is around
  1871.             // before recording.
  1872.             tempHandle = NewHandle(kPrefBufferSize);
  1873.             anErr = MemError();
  1874.             if (anErr != noErr)
  1875.                 {
  1876.                 tempHandle = NewHandle(kMinBufferSize);
  1877.                 anErr = MemError();
  1878.                 DisposeHandle(tempHandle);
  1879.                 tempHandle = nil;
  1880.                 }
  1881.             
  1882.             // if the preflight goes okay, do the record
  1883.             if (anErr == noErr)
  1884.                 {
  1885.                 Point    where = {50, 100};
  1886.                 
  1887.                 anErr = SndRecord(nil, where, siGoodQuality, (SndListHandle*) &tempHandle);
  1888.                 if (anErr == noErr)
  1889.                     {
  1890.                     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1891.                     ((TextDataPtr) pData)->soundHandle = tempHandle;
  1892.                     
  1893.                     SetDocumentContentChanged( (WindowDataPtr) pData, true );
  1894.                     }
  1895.                 else
  1896.                     DisposeHandle(tempHandle);
  1897.                     
  1898.                 if (anErr == userCanceledErr)
  1899.                     anErr = noErr;
  1900.                 }
  1901.             }
  1902.             break;
  1903.             
  1904.         case cPlay:
  1905.             if (((TextDataPtr) pData)->soundHandle)
  1906.                 (void) SndPlay(nil, (SndListHandle) ((TextDataPtr) pData)->soundHandle, false);
  1907.             break;
  1908.  
  1909.         case cErase:
  1910.             DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1911.             ((TextDataPtr) pData)->soundHandle = nil;
  1912.             
  1913.             SetDocumentContentChanged( (WindowDataPtr) pData, true );
  1914.             break;
  1915. #endif
  1916.             
  1917.         case cSpeak:
  1918.             DisposeOfSpeech(false);
  1919.             if (gSpeechChannel == nil)
  1920.                 anErr = NewSpeechChannel( &gCurrentVoice, &gSpeechChannel );
  1921.                 
  1922.             if ( anErr == noErr )
  1923.                 {
  1924.                 short    textLength, textStart;
  1925.                                     
  1926.                 // determine which text to speak
  1927.                 if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  1928.                     {
  1929.                     textLength = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  1930.                     textStart = (**((TextDataPtr) pData)->hTE).selStart;
  1931.                     }
  1932.                 else                                            // No text selected.
  1933.                     {
  1934.                     textLength = (**((TextDataPtr) pData)->hTE).teLength;
  1935.                     textStart = 0;
  1936.                     }
  1937.                     
  1938.                     
  1939.                 gSpeakPtr = NewPtr(textLength);
  1940.                 anErr = MemError();
  1941.                 if (anErr == noErr)
  1942.                     {
  1943.                     BlockMoveData( *((**((TextDataPtr) pData)->hTE).hText) + textStart, gSpeakPtr, (Size) textLength );
  1944.                     anErr = SpeakText( gSpeechChannel, gSpeakPtr, textLength );
  1945.                     }
  1946.                 }
  1947.             break;
  1948.  
  1949.         case cStopSpeaking:
  1950.             DisposeOfSpeech(true);
  1951.             break;
  1952.             
  1953.         case cSelectVoiceSubMenu:
  1954.                 {
  1955.                 VoiceSpec    newSpec;
  1956.                 short        i, menuIndex;
  1957.                 Str255        itemText;
  1958.                 short        theVoiceCount;
  1959.                 MenuHandle     menu = GetMenuHandle(mVoices);
  1960.                 
  1961.                 // in order to change voices, we need to ditch the speaking
  1962.                 DisposeOfSpeech(true);
  1963.  
  1964.                 // get the name of the selected voice                
  1965.                 menuIndex = menuResult & 0xFFFF;
  1966.                 GetMenuItemText(menu, menuIndex, itemText);
  1967.                 
  1968.                 if (CountVoices( &theVoiceCount ) == noErr)
  1969.                     {
  1970.                     VoiceDescription    description;        // Info about a voice.
  1971.         
  1972.                     for (i = 1; i <= theVoiceCount; ++i)
  1973.                         {
  1974.                         if ( (GetIndVoice( i, &newSpec ) == noErr)  &&
  1975.                              (GetVoiceDescription( &newSpec, &description, sizeof(description) ) == noErr ) )
  1976.                             {
  1977.                             if (PLstrcmp( itemText, description.name ) == 0)
  1978.                                 break;
  1979.                             }
  1980.                         }
  1981.                     }
  1982.                     
  1983.                 gCurrentVoice = newSpec;
  1984.                 for (i = CountMenuItems(menu); i >= 1; --i)
  1985.                     MacCheckMenuItem(menu, i, (menuIndex == i));
  1986.                 }
  1987.             break;
  1988.         
  1989.         case cSelectContents:
  1990.                 {
  1991.                 Str255    searchStr;
  1992.                 short    menuIndex;
  1993.                 long     newStart, newEnd;
  1994.                 
  1995.                 menuIndex = menuResult & 0xFFFF;
  1996.  
  1997.                 // get the search string for this menu item
  1998.                 anErr = TextGetContentsListItem(pData, menuIndex, nil, searchStr, nil);
  1999.                 
  2000.                 if (anErr == noErr)
  2001.                     {
  2002.                     if (PerformSearch(
  2003.                         (**((TextDataPtr) pData)->hTE).hText,
  2004.                         0,            // start at beginning of text
  2005.                         searchStr,
  2006.                         false,        // not case sensitive
  2007.                         false,        // forwards
  2008.                         false,        // wrap
  2009.                         &newStart, 
  2010.                         &newEnd))
  2011.                         {
  2012.                         
  2013.                         // <7>
  2014.                         
  2015.                         short     amount;
  2016.                         Point    newSelectionPt;
  2017.                         
  2018.                         // get QuickDraw offset of found text,  
  2019.                         // scroll that amount plus a line height,
  2020.                         // and add a fifth of the window for aesthetics (and  
  2021.                         // for slop to avoid fraction-of-line problems)
  2022.  
  2023.                         newSelectionPt = TEGetPoint(newEnd, ((TextDataPtr) pData)->hTE);
  2024.                         
  2025.                         amount = - newSelectionPt.v + pData->vScrollAmount;
  2026.                         amount += (pData->contentRect.bottom - pData->contentRect.top) / 5;
  2027.                         
  2028.                         SetControlAndClipAmount(pData->vScroll, &amount);
  2029.                         if (amount != 0)
  2030.                             {
  2031.                             DoScrollContent(pWindow, pData, 0, amount);
  2032.                             }
  2033.  
  2034.                         // move selection to beginning of found text 
  2035.                         // (are the Adjust calls necessary?)
  2036.                         
  2037.                         TESetSelect(newStart, newStart, ((TextDataPtr) pData)->hTE);    
  2038.                         AdjustTE(pData, false);
  2039.                         AdjustScrollBars(pWindow, false, false, nil);
  2040.  
  2041.                         }
  2042.                     else
  2043.                         {
  2044.                             // search failed
  2045.                             SysBeep(10);
  2046.                         }
  2047.                     }
  2048.                 }
  2049.             break;
  2050.         }
  2051.         
  2052.     return anErr;
  2053.     
  2054. } // TextCommand
  2055.  
  2056. // --------------------------------------------------------------------------------------------------------------
  2057.  
  2058. static OSStatus    TextPreMenuAccess(WindowPtr pWindow, WindowDataPtr pData)
  2059. {
  2060. #pragma unused (pWindow, pData)
  2061.  
  2062.     TextAddVoices();
  2063.  
  2064.     return noErr;
  2065. }
  2066.  
  2067. // --------------------------------------------------------------------------------------------------------------
  2068.  
  2069. static OSStatus    TextAdjustMenus(WindowPtr pWindow, WindowDataPtr pData)
  2070. {
  2071. #pragma unused (pWindow)
  2072.  
  2073.     // enable the commands that we support for editable text document
  2074.     if (pData->originalFileType == 'TEXT')
  2075.         {        
  2076.         if (((TextDataPtr) pData)->prevCommandID != cNull)
  2077.             EnableCommand(cUndo);
  2078.             
  2079.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2080.             {
  2081.             EnableCommand(cCut);
  2082.             EnableCommand(cCopy);
  2083.             EnableCommand(cClear);
  2084.  
  2085.             EnableCommand(cExecute);    // for AppleScript
  2086.             
  2087.             EnableCommand(cFindSelection);
  2088.             }
  2089.         
  2090.         TEFromScrap();
  2091.         if (TEGetScrapLength() > 0)
  2092.             EnableCommand(cPaste);
  2093.             
  2094.         EnableCommand(cSaveAs);
  2095.         EnableCommand(cSelectAll);
  2096.         
  2097.         EnableCommand(cFind);
  2098.         EnableCommand(cReplace);
  2099.         if (gFindString[0] != 0)
  2100.             {
  2101.             EnableCommand(cFindAgain);
  2102.             EnableCommand(cReplaceAgain);
  2103.             }
  2104.             
  2105.         // enable all fonts, select the font current, if that's what's best
  2106.         EnableCommand(cSelectFont);
  2107.         {
  2108.         short        mode = doFont;
  2109.         Str255        fontName, itemName;
  2110.         Str255        styleName;
  2111.         TextStyle    theStyle;
  2112.         Boolean        isCont;
  2113.         
  2114.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2115.         if (isCont)
  2116.             {
  2117.                 GetFontName(theStyle.tsFont, fontName);
  2118.             }
  2119.  
  2120.  
  2121.             {
  2122.             MenuHandle    menu = GetMenuHandle( mFont );
  2123.             short        count = CountMenuItems(menu);
  2124.             short        index;
  2125.             
  2126.             for (index = 1; index <= count; ++index)
  2127.                 {
  2128.                 short    mark;
  2129.                 
  2130.                 GetItemMark(menu, index, &mark);
  2131.                 if (isCont)
  2132.                     {
  2133.                     GetMenuItemText( menu, index, itemName );
  2134.                     
  2135.                     // don't change the checkmark if it's a heirarchichal menu, because
  2136.                     // the mark actually holds the ID of sub-menu
  2137.                     if ((mark == noMark) || (mark == checkMark))
  2138.                         {
  2139.                         MacCheckMenuItem(menu, index, EqualString(itemName, fontName, true, true) );
  2140.                         }
  2141.                     else
  2142.                         {
  2143.                         // if it is a sub menu, we check there too
  2144.                         MenuHandle    subMenu = GetMenuHandle(mark);
  2145.                         short        subCount = CountMenuItems(subMenu);
  2146.                         short        subIndex;
  2147.                         
  2148.                         if (EqualString(itemName, fontName, true, true))
  2149.                             {
  2150.                             SetItemStyle(menu, index, underline);
  2151.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2152.                                 {
  2153.                                 GetMenuItemText(subMenu, subIndex, itemName);
  2154.                                 MacCheckMenuItem(subMenu, subIndex, EqualString(itemName, styleName, true, true) );
  2155.                                 }
  2156.                             }
  2157.                         else
  2158.                             {
  2159.                             SetItemStyle(menu, index, normal);
  2160.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2161.                                 MacCheckMenuItem(subMenu, subIndex, false );
  2162.                             }
  2163.                         }
  2164.                     }
  2165.                 else
  2166.                     {
  2167.                     if ((mark == noMark) || (mark == checkMark))
  2168.                         MacCheckMenuItem(menu, index, false);
  2169.                     else
  2170.                         SetItemStyle(menu, index, normal);
  2171.                     }
  2172.                 }
  2173.             }
  2174.         }
  2175.  
  2176.         // enable the sizes, and outline what's currently valid
  2177.         {
  2178.         short        mode;
  2179.         TextStyle    theStyle;
  2180.         Boolean        isCont;
  2181.         short        whichToCheck;
  2182.         
  2183.         // find out the continuous run of sizes
  2184.         whichToCheck = 0;
  2185.         mode = doSize;
  2186.         if (TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2187.             {            
  2188.             whichToCheck = theStyle.tsSize;
  2189.             
  2190.             // default font size -> proper size
  2191.             if (whichToCheck == 0)
  2192.                 whichToCheck = GetDefFontSize();
  2193.             }
  2194.             
  2195.         // find out the font runs
  2196.         mode = doFont;
  2197.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2198.         
  2199.         EnableCommandCheckStyle(cSize9,  whichToCheck == 9, (isCont & RealFont(theStyle.tsFont, 9)) ? outline : normal);
  2200.         EnableCommandCheckStyle(cSize10, whichToCheck == 10, (isCont & RealFont(theStyle.tsFont, 10)) ? outline : normal);
  2201.         EnableCommandCheckStyle(cSize12, whichToCheck == 12, (isCont & RealFont(theStyle.tsFont, 12)) ? outline : normal);
  2202.         EnableCommandCheckStyle(cSize14, whichToCheck == 14, (isCont & RealFont(theStyle.tsFont, 14)) ? outline : normal);
  2203.         EnableCommandCheckStyle(cSize18, whichToCheck == 18, (isCont & RealFont(theStyle.tsFont, 18)) ? outline : normal);
  2204.         EnableCommandCheckStyle(cSize24, whichToCheck == 24, (isCont & RealFont(theStyle.tsFont, 24)) ? outline : normal);
  2205.         EnableCommandCheckStyle(cSize36, whichToCheck == 36, (isCont & RealFont(theStyle.tsFont, 36)) ? outline : normal);            
  2206.         }
  2207.         
  2208.         {
  2209.         short        mode = doFace;
  2210.         TextStyle    theStyle;
  2211.         Style        legalStyles;
  2212.         
  2213.         if (!TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2214.             {
  2215.             theStyle.tsFace = normal;
  2216.             EnableCommandCheck(cPlain, false);
  2217.             }
  2218.         else
  2219.             EnableCommandCheck(cPlain, theStyle.tsFace == normal);
  2220.             
  2221.         // <39> use the script manager to determine legal styles for this
  2222.         // run of text.  If the legal styles are zero (trap unimplemented),
  2223.         // then we assume all styles.
  2224.         legalStyles = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptValidStyles);
  2225.         if (legalStyles == 0)
  2226.             legalStyles = (Style)0xFFFF;
  2227.             
  2228.         if (legalStyles & bold)
  2229.             EnableCommandCheck(cBold,             theStyle.tsFace & bold);
  2230.         if (legalStyles & italic)
  2231.             EnableCommandCheck(cItalic,         theStyle.tsFace & italic);
  2232.         if (legalStyles & underline)
  2233.             EnableCommandCheck(cUnderline,         theStyle.tsFace & underline);
  2234.         if (legalStyles & outline)
  2235.             EnableCommandCheck(cOutline,         theStyle.tsFace & outline);
  2236.         if (legalStyles & shadow)
  2237.             EnableCommandCheck(cShadow,         theStyle.tsFace & shadow);
  2238.         if (legalStyles & condense)
  2239.             EnableCommandCheck(cCondensed,         theStyle.tsFace & condense);
  2240.         if (legalStyles & extend)
  2241.             EnableCommandCheck(cExtended,         theStyle.tsFace & extend);
  2242.         }
  2243.         
  2244.         }
  2245.  
  2246.     // enable commands related to speaking the content if we have support for that
  2247.     if (gMachineInfo.haveTTS)
  2248.         {
  2249.         // note that none of this code should depend on the actual contents of the voices menu,
  2250.         // since the voices thread might not have finished setting up the menu yet
  2251.  
  2252.         // if we are speaking, we can stop
  2253.         if (gSpeechChannel) 
  2254.             EnableCommand(cStopSpeaking);
  2255.  
  2256.         // even while speaking, you can re-speak or select a new voice
  2257.         EnableCommand(cSpeak);
  2258.         EnableCommand(cSelectVoice);
  2259.         EnableCommand(cSelectVoiceSubMenu);
  2260.         
  2261.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2262.             ChangeCommandName(cSpeak, kTextStrings, iSpeakSelection);
  2263.         else
  2264.             ChangeCommandName(cSpeak, kTextStrings, iSpeakAll);
  2265.             
  2266.         }
  2267.         
  2268.     // enable the correct controls to go with sound input/output
  2269.     if (((TextDataPtr) pData)->soundHandle)
  2270.         EnableCommand(cPlay);
  2271.     if (pData->originalFileType == 'TEXT')
  2272.         {
  2273.         if (((TextDataPtr) pData)->soundHandle)
  2274.             EnableCommand(cErase);
  2275.         else
  2276.             {
  2277. #if 0
  2278.             if (gMachineInfo.haveRecording)
  2279.                 EnableCommand(cRecord);
  2280. #endif
  2281.             }
  2282.         }
  2283.     
  2284.     // enable the contents menu, if any         
  2285.     (void) TextAdjustContentsMenu(pData);
  2286.     
  2287.     // enable commands that we support at all times
  2288.     if (GetControlMaximum(pData->vScroll) != 0)
  2289.         {
  2290.         EnableCommand(cNextPage);
  2291.         EnableCommand(cPreviousPage);
  2292.         }
  2293.     
  2294.     return noErr;
  2295.     
  2296. } // TextAdjustMenus
  2297.  
  2298. // --------------------------------------------------------------------------------------------------------------
  2299.  
  2300. static OSStatus    TextGetDocumentRect(WindowPtr pWindow, WindowDataPtr pData, 
  2301.             LongRect * documentRectangle, Boolean forGrow)
  2302. {
  2303. #pragma unused (pWindow)
  2304.     
  2305.     Rect    theRect = pData->contentRect;
  2306.     Rect    maxRect;
  2307.     
  2308.     GetRegionBounds( GetGrayRgn(), &maxRect );
  2309.     
  2310.     if ( (!forGrow) && (!(((TextDataPtr) pData)->insideClickLoop) ) )
  2311.         RecalcTE(pData, false);
  2312.     
  2313.     theRect.bottom = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  2314.     theRect.bottom += kMargins*2;
  2315.     theRect.right = maxRect.right;
  2316.         
  2317.     if (theRect.bottom < pData->contentRect.bottom)
  2318.         theRect.bottom = pData->contentRect.bottom;
  2319.         
  2320.     if (forGrow)
  2321.         theRect.bottom = maxRect.bottom-kScrollBarSize;
  2322.         
  2323.     RectToLongRect(&theRect, documentRectangle);
  2324.     
  2325.     return noErr;
  2326.     
  2327. } // TextGetDocumentRect
  2328.  
  2329. // --------------------------------------------------------------------------------------------------------------
  2330.  
  2331. static OSStatus    TextGetBalloon(WindowPtr pWindow, WindowDataPtr pData, 
  2332.         Point *localMouse, short * returnedBalloonIndex, Rect *returnedRectangle)
  2333. {
  2334. #pragma unused (pWindow, pData, localMouse, returnedRectangle)
  2335.  
  2336.     *returnedBalloonIndex = iHelpTextContent;
  2337.     
  2338.     return noErr;
  2339.     
  2340. } // TextGetBalloon
  2341.  
  2342. // --------------------------------------------------------------------------------------------------------------
  2343.  
  2344. static long TextCalculateIdleTime(WindowPtr pWindow, WindowDataPtr pData)
  2345. {
  2346. #pragma unused (pWindow, pData)
  2347.  
  2348.     if ( (gMachineInfo.amInBackground) || (! (**(((TextDataPtr) pData)->hTE)).active) )
  2349.         return(0x7FFFFFF);
  2350.     else
  2351.         return(GetCaretTime());
  2352.         
  2353. } // TextCalculateIdleTime
  2354.  
  2355. // --------------------------------------------------------------------------------------------------------------
  2356.  
  2357. static OSStatus    TextAdjustCursor(WindowPtr pWindow, WindowDataPtr pData, 
  2358.                         Point * localMouse, RgnHandle globalRgn)
  2359. {
  2360. #pragma unused (pWindow)
  2361.  
  2362.     OSStatus            anErr = noErr;
  2363.     CursHandle        theCross;
  2364.     RgnHandle        hilightRgn;
  2365.     Cursor            arrow;
  2366.  
  2367.     if (gMachineInfo.haveDragMgr)
  2368.         {
  2369.         RgnHandle globalHilight;
  2370.         
  2371.         hilightRgn = NewRgn();
  2372.         TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  2373.         
  2374.         globalHilight = NewRgn();
  2375.         CopyRgn(hilightRgn, globalHilight);
  2376.         LocalToGlobalRgn(globalHilight);
  2377.         
  2378.         if (PtInRgn(*localMouse, hilightRgn))
  2379.             {
  2380.             // we're already in the hilight rgn, so we don't need mouse-moved events as long
  2381.             // as we stay there
  2382.             CopyRgn(globalHilight, globalRgn);
  2383.             
  2384.             SetCursor(GetQDGlobalsArrow(&arrow));
  2385.             DisposeRgn(hilightRgn);
  2386.             return eActionAlreadyHandled;    
  2387.             }
  2388.         else if (!EmptyRgn(hilightRgn))
  2389.             {
  2390.             // make sure we get mouse-moved events if the mouse moves into the hilight rgn,
  2391.             // so that we can change the cursor
  2392.             DiffRgn(globalRgn, globalHilight, globalRgn);
  2393.             }
  2394.     
  2395.         DisposeRgn(globalHilight);
  2396.         DisposeRgn(hilightRgn);
  2397.         }
  2398.         
  2399.     theCross = MacGetCursor(iBeamCursor);
  2400.     if (theCross)
  2401.         {
  2402.         char    oldState;
  2403.         
  2404.         oldState = HGetState((Handle) theCross);
  2405.         HLock((Handle) theCross);
  2406.         SetCursor(*theCross);
  2407.         HSetState((Handle) theCross, oldState);
  2408.         anErr = eActionAlreadyHandled;
  2409.         }
  2410.         
  2411.     return anErr;
  2412.     
  2413. } // TextAdjustCursor
  2414.  
  2415. // --------------------------------------------------------------------------------------------------------------
  2416.  
  2417. short gNilCaretProc[] = { 
  2418.                         0x584F,         // ADDQ.W    #$4, A7
  2419.                         0x4E75};        // RTS
  2420.  
  2421. // --------------------------------------------------------------------------------------------------------------
  2422.  
  2423. static OSStatus    TextPrintPage(WindowPtr pWindow, WindowDataPtr pData,
  2424.                     Rect * pageRect, long *pageNum)
  2425. {
  2426. #pragma unused (pWindow)
  2427.  
  2428.     OSStatus        anErr = noErr;
  2429.     short        footerHeight;
  2430.     TEHandle    hTE;
  2431.     Rect        areaForThisPage;
  2432.     short        ourPage = 1;
  2433.     Boolean        documentHasFormControl = Count1Resources(kFormResource) != 0;
  2434.     
  2435.     // calculate area for the footer (page number)
  2436.     {
  2437.     FontInfo    theInfo;
  2438.     
  2439.     TextFont(0);
  2440.     TextSize(0);
  2441.     TextFace(normal);
  2442.     GetFontInfo(&theInfo);
  2443.     footerHeight = (theInfo.ascent + theInfo.descent + theInfo.leading) << 1;
  2444.     }
  2445.     
  2446.     // duplicate the text edit record, disable the selection before swapping the new port in
  2447.     hTE = ((TextDataPtr) pData)->hTE;
  2448.     TEDeactivate(hTE);
  2449.     
  2450.     anErr = HandToHand((Handle*) &hTE);
  2451.     nrequire(anErr, DuplicateTE);
  2452.  
  2453.     // turn off outline hilighting -- because the window is disabled while
  2454.     // printing is going on, but we don't want that disabled hilight to draw
  2455.     TEFeatureFlag(teFOutlineHilite, teBitClear, ((TextDataPtr) pData)->hTE);
  2456.         
  2457.     // now HERE'S a real hack!  Under certain conditions, Text Edit will draw the
  2458.     // cursor, even if you said the edit record is inactive!  This happens when
  2459.     // the internal state sez that the cursor hasn't been drawn yet.  Lucky
  2460.     // for us, the caret is drawn through a hook, which we replace with a NOP.
  2461.     (**hTE).caretHook = (CaretHookUPP) gNilCaretProc;
  2462.     
  2463.     // point the rectangles to be the page rect minus the footer
  2464.     areaForThisPage = *pageRect;
  2465.     areaForThisPage.bottom -= footerHeight;
  2466.     
  2467.     InsetRect(&areaForThisPage, kPrintMargins, kPrintMargins);
  2468.     (**hTE).viewRect = (**hTE).destRect = areaForThisPage;
  2469.  
  2470.     // recalculate the line breaks
  2471.     TECalText(hTE);
  2472.  
  2473.     // point it at the printing port.
  2474.     (**hTE).inPort = GetQDGlobalsThePort();
  2475.  
  2476.     // now loop over all pages doing page breaking until we find our current
  2477.     // page, which we print, and then return.
  2478.     {
  2479.     Rect    oldPageHeight = (**hTE).viewRect;
  2480.     short    currentLine = 0;
  2481.     long    prevPageHeight = 0;
  2482.  
  2483.     while (ourPage <= *pageNum)
  2484.         {
  2485.         long    currentPageHeight = 0;
  2486.                     
  2487.         // calculate the height including the current page, breaks
  2488.         // when one of three things happen:
  2489.         // 1) adding another line to this page would go beyond the length of the page
  2490.         // 2) a picture needs to be broken to the next page (NOT YET IMPLEMENTED)
  2491.         // 3) we run out of lines for the document 
  2492.         // 4) if the line has a page break (defined as a non breaking space w/o a PICT)
  2493.  
  2494.         // POTENTIAL BUG CASES:
  2495.         // If a single line > the page height.  Can that happen?  If so, we need to 
  2496.         // add something to handle it.
  2497.         do
  2498.             {
  2499.             long currentLineHeight;
  2500.             
  2501.             // zero based count -- but one based calls to TEGetHeight 
  2502.             currentLineHeight = TEGetHeight(currentLine+1, currentLine+1, hTE);
  2503.                 
  2504.             // if adding this line would just be too much, break out of here
  2505.             if ((currentLineHeight + currentPageHeight) > (areaForThisPage.bottom - areaForThisPage.top))
  2506.                 break;
  2507.                 
  2508.             ++ currentLine;
  2509.             currentPageHeight += currentLineHeight;
  2510.             
  2511.             // if this line had a page break on it, break out of pagination
  2512.             if (documentHasFormControl && LineHasPageBreak(currentLine-1, hTE))
  2513.                 break;
  2514.                 
  2515.             } while (currentLine < (**hTE).nLines);
  2516.         
  2517.         // if this the page we are trying to print
  2518.         if (ourPage == *pageNum)
  2519.             {
  2520.             Str255        pageString;
  2521.             RgnHandle    oldRgn = NewRgn();
  2522.             
  2523.             // move onto the next page via offset by the previous pages -- but
  2524.             // clip to the current page height because we wouldn't want to see
  2525.             // half of a line from the next page at the bottom of a page.
  2526.             OffsetRect(&oldPageHeight, 0, -(prevPageHeight));
  2527.             oldPageHeight.bottom = oldPageHeight.top + currentPageHeight;
  2528.             (**hTE).destRect = oldPageHeight;
  2529.             
  2530.             // clip to this area as well
  2531.             areaForThisPage.bottom = areaForThisPage.top + currentPageHeight;
  2532.             GetClip(oldRgn);
  2533.             ClipRect(&areaForThisPage);
  2534.             
  2535.             // draw the edit record, plus our cool pictures    
  2536.             TEUpdate(&areaForThisPage, hTE); 
  2537.             DrawPictures(pData, hTE);
  2538.             
  2539.             // restore the clip
  2540.             SetClip(oldRgn);
  2541.             DisposeRgn(oldRgn);
  2542.             
  2543.             // Draw the page string at the bottom of the page, centered
  2544.             pageString[0] = 2;
  2545.             pageString[1] = '-';
  2546.             NumToString(*pageNum, &pageString[2]);
  2547.             pageString[0] += pageString[2];
  2548.             pageString[2] = ' ';
  2549.             pageString[++pageString[0]] = ' ';
  2550.             pageString[++pageString[0]] = '-';
  2551.             
  2552.             MoveTo(
  2553.                 pageRect->left + 
  2554.                     ((pageRect->right - pageRect->left) >> 1) - 
  2555.                     (StringWidth(pageString)>>1),
  2556.                 pageRect->bottom - kPrintMargins);
  2557.                 
  2558.             DrawString(pageString);
  2559.             
  2560.             // if we have completed all pages
  2561.             if (currentLine >= (**hTE).nLines)
  2562.                 {
  2563.                 // tell it to stop printing
  2564.                 *pageNum = -1;
  2565.                 }
  2566.                 
  2567.             // get out of here!
  2568.             break;
  2569.             }
  2570.     
  2571.         // move onto the next page via count
  2572.         ++ourPage;
  2573.         
  2574.         // and the list of pages before now includes this page we just finished
  2575.         prevPageHeight += currentPageHeight;
  2576.         
  2577.         }
  2578.     
  2579.     }
  2580.     
  2581.     
  2582.     // restore text for visible page if done
  2583.     if (*pageNum == -1)
  2584.         {
  2585.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  2586.         TECalText(((TextDataPtr) pData)->hTE);
  2587.         if (pData->originalFileType != 'ttro')
  2588.             TEActivate(((TextDataPtr) pData)->hTE);
  2589.         }
  2590.         
  2591. // FALL THROUGH EXCEPTION HANDLING
  2592.  
  2593.     // Dispose this way to avoid disposing of any owned objects
  2594.     DisposeHandle((Handle) hTE);
  2595. DuplicateTE:
  2596.  
  2597.     return anErr;
  2598.     
  2599. } // TextPrintPage
  2600.  
  2601. // --------------------------------------------------------------------------------------------------------------
  2602.  
  2603. static OSStatus    TextInitData(WindowDataPtr pData)
  2604. {
  2605.     static TEClickLoopUPP gMyClickLoop = NULL;
  2606.     OSStatus                anErr = noErr;
  2607.  
  2608.     if (!gMyClickLoop) {
  2609.         gMyClickLoop = NewTEClickLoopUPP(CClikLoop);
  2610.     }
  2611.  
  2612.     pData->bumpUntitledCount    = true;
  2613.     
  2614.     pData->pScrollContent         = (ScrollContentProc)    TextScrollContent;
  2615.     pData->pAdjustSize             = (AdjustSizeProc)        TextAdjustSize;
  2616.     pData->pGetDocumentRect     = (GetDocumentRectProc)    TextGetDocumentRect;
  2617.     pData->pPreMenuAccess        = (PreMenuAccessProc)    TextPreMenuAccess;
  2618.     pData->pAdjustMenus             = (AdjustMenusProc)        TextAdjustMenus;
  2619.     pData->pCommand                 = (CommandProc)            TextCommand;
  2620.  
  2621.     pData->pCloseWindow         = (CloseWindowProc)        TextCloseWindow;
  2622.     pData->pFilterEvent         = (FilterEventProc)        TextFilterEvent;
  2623.     pData->pActivateEvent         = (ActivateEventProc)    TextActivateEvent;
  2624.     pData->pUpdateWindow         = (UpdateWindowProc)    TextUpdateWindow;
  2625.     pData->pPrintPage             = (PrintPageProc)        TextPrintPage;
  2626.     pData->pSaveTo                = (SaveToProc)            TextSaveTo;
  2627.  
  2628.     // we only support keydowns and editing for modifable docs
  2629.     if (pData->originalFileType != 'ttro')
  2630.         {
  2631.         pData->pKeyEvent             = (KeyEventProc)            TextKeyEvent;
  2632.         pData->pContentClick        = (ContentClickProc)        TextContentClick;
  2633.         pData->pAdjustCursor        = (AdjustCursorProc)        TextAdjustCursor;
  2634.         pData->pGetBalloon            = (GetBalloonProc)            TextGetBalloon;
  2635.         pData->pCalculateIdleTime    = (CalculateIdleTimeProc)    TextCalculateIdleTime;
  2636.         
  2637.         // We can always reference our Drag handlers, because they will not be called if we
  2638.         // don't have the Drag Manager available. We needn't check here (it would be redundant).
  2639.         pData->pDragTracking        = (DragTrackingProc)    TextDragTracking;
  2640.         pData->pDragReceive            = (DragReceiveProc)        TextDragReceive;
  2641.  
  2642.         pData->documentAcceptsText    = true;
  2643.         }
  2644.         
  2645.     // leave room for the grow area at bottom
  2646.     pData->hasGrow                = true;
  2647. #if 0
  2648.     //    This is already taken care of in MakeNewWindow (SimpleText.c)
  2649.     //    Look for "thePreflight.wantHScroll"
  2650.     pData->contentRect.bottom    -= kScrollBarSize;
  2651. #endif
  2652.     if ((pData->contentRect.right - pData->contentRect.left) > kOnePageWidth)
  2653.         pData->contentRect.right = pData->contentRect.left + kOnePageWidth;
  2654.         
  2655.     ((TextDataPtr) pData)->hTE    = TEStyleNew(&pData->contentRect, &pData->contentRect);
  2656.     anErr = MemError();
  2657.     nrequire(anErr, TENewFailed);
  2658.         
  2659.     pData->hScrollAmount        = 0;
  2660.     pData->vScrollAmount        = TEGetHeight(0, 0, ((TextDataPtr) pData)->hTE);
  2661.  
  2662.     TEAutoView(true, ((TextDataPtr) pData)->hTE);
  2663.  
  2664.     // Setup our click loop to handle autoscrolling
  2665.     ((TextDataPtr) pData)->docClick = (**(((TextDataPtr) pData)->hTE)).clickLoop;
  2666.     TESetClickLoop(gMyClickLoop, ((TextDataPtr) pData)->hTE);
  2667.     
  2668.     // initalize undo information
  2669.     ((TextDataPtr) pData)->prevCommandID = cNull;
  2670.  
  2671.     return noErr;
  2672.     
  2673. TENewFailed:
  2674.  
  2675.         DebugStr("\pTextFile: TEStyleNew returned an error." );
  2676.  
  2677.     return anErr;
  2678.     
  2679. } // TextInitData
  2680.  
  2681. // --------------------------------------------------------------------------------------------------------------
  2682.  
  2683. static OSStatus    TextReadDataFork(WindowDataPtr pData)
  2684. {    
  2685.     OSStatus    anErr = noErr;
  2686.     
  2687.     // if we have a data fork, read the contents into the record    
  2688.     if (pData->dataRefNum != -1)
  2689.         {
  2690.         long    dataSize;
  2691.         
  2692.         GetEOF(pData->dataRefNum, &dataSize);
  2693.         if (dataSize > kMaxLength)
  2694.             anErr = eDocumentTooLarge;
  2695.         else
  2696.             {
  2697.             Handle    tempHandle = NewHandle(dataSize);
  2698.             anErr = MemError();
  2699.             if (anErr == noErr)
  2700.                 {
  2701.                 int i;
  2702.  
  2703.                 // read the text in
  2704.                 SetFPos(pData->dataRefNum, fsFromStart, 0);
  2705.                 anErr = FSRead(pData->dataRefNum, &dataSize, * tempHandle);                
  2706.                 for (i=0; i < dataSize; i++) {
  2707.                     if ((*tempHandle)[i] == '\n')
  2708.                         (*tempHandle)[i] = '\r';
  2709.                 }
  2710.  
  2711.                 // then insert it.
  2712.                 if (anErr == noErr)
  2713.                     {
  2714.                     HLock(tempHandle);
  2715.                     TEStyleInsert(*tempHandle, dataSize, nil, ((TextDataPtr) pData)->hTE);
  2716.                     anErr = MemError();
  2717.                     }
  2718.                 DisposeHandle(tempHandle);
  2719.                 }
  2720.             
  2721.             }
  2722.         
  2723.         }
  2724.     nrequire(anErr, ReadData);
  2725.     
  2726.     return noErr;
  2727.     
  2728. ReadData:
  2729.  
  2730.     return anErr;
  2731.     
  2732. } // TextReadDataFork
  2733.  
  2734. // --------------------------------------------------------------------------------------------------------------
  2735.  
  2736. static void        TextReadResourceFork(WindowDataPtr pData)
  2737. {    
  2738.     // if we have a resource fork, read the contents
  2739.     if (pData->resRefNum != -1)
  2740.         {
  2741.         short    oldResFile = CurResFile();
  2742.         Handle    theStyle;
  2743.         
  2744.         // read the style information
  2745.         UseResFile(pData->resRefNum);
  2746.         theStyle = Get1Resource('styl', 128);
  2747.         if (theStyle)
  2748.             {
  2749.             HNoPurge(theStyle);
  2750.             TEUseStyleScrap(0, 32767, (StScrpHandle) theStyle, true, ((TextDataPtr) pData)->hTE);
  2751.             ReleaseResource(theStyle);
  2752.             }
  2753.  
  2754.         // if we have sound, load it in and detach it
  2755.         {
  2756.         Handle    soundHandle = Get1Resource('snd ', kSoundBase);
  2757.         if (soundHandle)
  2758.             {
  2759.             HNoPurge(soundHandle);
  2760.             DetachResource(soundHandle);
  2761.             ((TextDataPtr) pData)->soundHandle = soundHandle;
  2762.             }
  2763.         }
  2764.  
  2765.         UseResFile(oldResFile);
  2766.         }
  2767.             
  2768. } // TextReadResourceFork
  2769.  
  2770. // --------------------------------------------------------------------------------------------------------------
  2771.  
  2772. static void        TextFinishTESetup(WindowDataPtr pData)
  2773. {            
  2774.     // hook out drawing of the non-breaking space for read only documents,
  2775.     // for modifiable documents, enable outline hiliting (ie, when TE window
  2776.     // isn't in front, show the gray outline)
  2777.     if (pData->originalFileType == 'ttro')
  2778.         {
  2779.         static DrawHookUPP gMyDrawHook = NULL;
  2780.         UniversalProcPtr    hookRoutine;
  2781.  
  2782.         if (!gMyDrawHook) {
  2783.             gMyDrawHook = NewDrawHookUPP((DrawHookProcPtr)MyDrawHook);
  2784.         }
  2785.  
  2786.         hookRoutine = (UniversalProcPtr)gMyDrawHook;
  2787.         
  2788.         TECustomHook(intDrawHook, &hookRoutine, ((TextDataPtr) pData)->hTE);
  2789.         }
  2790.     else
  2791.         {
  2792.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  2793.         }
  2794.  
  2795.     // make a TSM document if this is editable
  2796.     if     (
  2797.         (pData->originalFileType != 'ttro') &&
  2798.         (gMachineInfo.haveTSMTE)
  2799.         )
  2800.         {
  2801.         OSType    supportedInterfaces[1];
  2802.  
  2803.         supportedInterfaces[0] = kTSMTEInterfaceType;
  2804.         
  2805.         if (NewTSMDocument(1, supportedInterfaces, 
  2806.             &pData->docTSMDoc, (long)&pData->docTSMRecHandle) == noErr)
  2807.             {
  2808.             static TSMTEPreUpdateUPP gTSMPreUpdateProc = NULL;
  2809.             static TSMTEPostUpdateUPP gTSMPostUpdateProc = NULL;
  2810.             long response;
  2811.  
  2812.             if (!gTSMPreUpdateProc) {
  2813.                 gTSMPreUpdateProc = NewTSMTEPreUpdateUPP(TSMPreUpdateProc);
  2814.             }
  2815.             if (!gTSMPostUpdateProc) {
  2816.                 gTSMPostUpdateProc = NewTSMTEPostUpdateUPP(TSMPostUpdateProc);
  2817.             }
  2818.  
  2819.             (**(pData->docTSMRecHandle)).textH                 = ((TextDataPtr) pData)->hTE;
  2820.             if ((Gestalt(gestaltTSMTEVersion, &response) == noErr) && (response == gestaltTSMTE1))
  2821.                 (**(pData->docTSMRecHandle)).preUpdateProc     = gTSMPreUpdateProc;
  2822.             (**(pData->docTSMRecHandle)).postUpdateProc     = gTSMPostUpdateProc;
  2823.             (**(pData->docTSMRecHandle)).updateFlag         = kTSMTEAutoScroll;
  2824.             (**(pData->docTSMRecHandle)).refCon             = (long)pData;
  2825.             }
  2826.         }
  2827.  
  2828.     // now we have added text, so adjust views and such as needed
  2829.     TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  2830.     RecalcTE(pData, true);
  2831.     AdjustTE(pData, true);
  2832.  
  2833.     // ???? Hack to get around a 7.0 TextEdit bug.  If you are pasting a multiple
  2834.     // line clipboard into TE, *and* the TextEdit record is new, *and* the selection
  2835.     // is at the begining of the doc (0,0 as above), *and* you haven't moved the
  2836.     // cursor around at all, then TE pastes thinking it's at the end of the line,
  2837.     // when it really should be at the begining.  Then if you <cr> with the cursor
  2838.     // visible, it'll leave a copy behind.  
  2839.     
  2840.     // I'm not happy with this, but I don't know another way around the problem.
  2841.     if (pData->originalFileType != 'ttro') 
  2842.         {
  2843.         TEKey(0x1F, ((TextDataPtr) pData)->hTE);
  2844.         TEKey(0x1E, ((TextDataPtr) pData)->hTE);
  2845.         ShowCursor ( ); // on 8, TEKey calls ObscureCursor
  2846.         }
  2847.     
  2848.     // <39> if this is a new document, convert the "system size", "system font", and
  2849.     // "application font" into real font IDs and sizes.  This is so that
  2850.     // if someone saves this document and opens it with another script
  2851.     // system, they don't get all huffy that the font changed on them.
  2852.     // It also solves problems with cut and paste to applications too stupid
  2853.     // to know that "zero" means system size.
  2854.     if (pData->dataRefNum == -1)
  2855.         {
  2856.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  2857.         short        mode = doAll;
  2858.         TextStyle    theStyle;
  2859.     
  2860.         TEContinuousStyle(&mode, &theStyle, hTE);
  2861.         if (theStyle.tsSize == 0)
  2862.             theStyle.tsSize = GetDefFontSize();
  2863.         if (theStyle.tsFont == systemFont)
  2864.             theStyle.tsFont = GetSysFont();
  2865.         if (theStyle.tsFont == applFont)
  2866.             theStyle.tsFont = GetAppFont();
  2867.             
  2868.         mode = doAll;
  2869.         TESetStyle(mode, &theStyle, false, hTE);
  2870.         }
  2871.  
  2872. /*    // To work around some of the font sizing
  2873.     // problems let's try to puppet string the
  2874.     // right result. 
  2875.     //DebugStr("\pSetting size to 18 point." );
  2876.  
  2877.     TESetSelect(0,32000,((TextDataPtr) pData)->hTE);
  2878.     {
  2879.         TEHandle        hTE = ((TextDataPtr) pData)->hTE;
  2880.                 short           mode = doAll;
  2881.         short        saveSize;
  2882.                 TextStyle       theStyle;
  2883.  
  2884.                 TEContinuousStyle(&mode, &theStyle, hTE);
  2885.                 if (theStyle.tsSize == 0)
  2886.                         theStyle.tsSize = GetDefFontSize();
  2887.                 if (theStyle.tsFont == systemFont)
  2888.                         theStyle.tsFont = GetSysFont();
  2889.                 if (theStyle.tsFont == applFont)
  2890.                         theStyle.tsFont = GetAppFont();
  2891.  
  2892.         saveSize = theStyle.tsSize;
  2893.         theStyle.tsSize = 18;
  2894.                 TESetStyle(doSize, &theStyle, true, hTE);
  2895.         TECalText(((TextDataPtr) pData)->hTE);
  2896.                AdjustTE(pData, false);
  2897.  
  2898.         //theStyle.tsSize = saveSize;
  2899.                 //TESetStyle(doSize, &theStyle, true, hTE);
  2900.         //TECalText(((TextDataPtr) pData)->hTE);
  2901.                //AdjustTE(pData, false);
  2902.     }
  2903.     TESetSelect(0,0,((TextDataPtr) pData)->hTE);
  2904.  
  2905.     //DebugStr( "\pDone setting size.\n" ); */
  2906.  
  2907. } // TextFinishTESetup
  2908.  
  2909. // --------------------------------------------------------------------------------------------------------------
  2910.  
  2911. static void        TextCloseStationery(WindowDataPtr pData)
  2912. {
  2913.     // if stationery, use untitled and close down the files
  2914.     if (pData->originalFileType == 'sEXT')
  2915.         {
  2916.         pData->originalFileType = 'TEXT';
  2917.         pData->openAsNew = true;
  2918.         if (pData->resRefNum != -1)
  2919.             CloseResFile(pData->resRefNum);
  2920.         if (pData->dataRefNum != -1)
  2921.             FSClose(pData->dataRefNum);
  2922.         pData->resRefNum = pData->dataRefNum = -1;
  2923.         }
  2924.         
  2925. } // TextCloseStationery
  2926.  
  2927. // --------------------------------------------------------------------------------------------------------------
  2928. static int        FindVoicePosition(MenuHandle voicesMenu, short menuCount, VoiceDescription* pdesc)
  2929. {
  2930.     short    item;
  2931.     
  2932.     for ( item = 1; item <= menuCount; ++item )
  2933.         {
  2934.         Str255    itemText;
  2935.         
  2936.         GetMenuItemText( voicesMenu, item, itemText );
  2937.         /*1st > 2nd*/
  2938.         if ( PLstrcmp( itemText, pdesc->name ) == 1 )
  2939.             break;                        // Found where name goes in list.
  2940.         }
  2941.         
  2942.     return item;
  2943.     
  2944. } // FindVoicePosition
  2945.  
  2946. // --------------------------------------------------------------------------------------------------------------
  2947.  
  2948. static void        TextAdd1Voice(short i, MenuHandle voicesMenu)
  2949. {
  2950.     VoiceSpec            spec;                // A voice to add to the menu.
  2951.     VoiceDescription    description;        // Info about a voice.
  2952.     short                item;
  2953.  
  2954.     if ( (GetIndVoice( i, &spec ) == noErr)  &&
  2955.          (GetVoiceDescription( &spec, &description, sizeof(description) ) == noErr ) )
  2956.         {
  2957.         short    menuCount = CountMenuItems( voicesMenu );
  2958.         
  2959.         // first one we are adding == get rid of item already there
  2960.         if ( (i == 1)  && (menuCount > 0) )
  2961.             {
  2962.             DeleteMenuItem( voicesMenu, 1 );
  2963.             --menuCount;
  2964.             }
  2965.             
  2966.         item = FindVoicePosition(voicesMenu, menuCount, &description);
  2967.  
  2968.         InsertMenuItem( voicesMenu, "\p ", item - 1 );
  2969.         SetMenuItemText( voicesMenu, item, description.name );
  2970.         
  2971.         MacCheckMenuItem(voicesMenu, item, 
  2972.             ((gCurrentVoice.creator == spec.creator) && (gCurrentVoice.id == spec.id)) );
  2973.         }
  2974.         
  2975. } // TextAdd1Voice
  2976.  
  2977. // --------------------------------------------------------------------------------------------------------------
  2978.  
  2979. void    TextAddVoices()
  2980. {
  2981. #pragma unused(threadParam)
  2982.  
  2983.     short    theVoiceCount;
  2984.     
  2985.     if (gAddedVoices)
  2986.         return;
  2987.     
  2988.     if (gMachineInfo.haveTTS && CountVoices( &theVoiceCount ) == noErr)
  2989.         {
  2990.         OSStatus                anErr;
  2991.         VoiceDescription    description;        // Info about a voice.
  2992.         MenuHandle            voicesMenu = GetMenuHandle(mVoices);
  2993.         
  2994.         anErr = GetVoiceDescription( nil, &description, sizeof(description) );
  2995.         if (anErr == noErr)
  2996.             {
  2997.             int        i;
  2998.             
  2999.             gCurrentVoice = description.voice;
  3000.             
  3001.             for (i = 1; i <= theVoiceCount; ++i)
  3002.                 {
  3003.                 TextAdd1Voice(i, voicesMenu);
  3004.                 }
  3005.             }
  3006.         }
  3007.     
  3008.     gAddedVoices = true;
  3009.  
  3010.     return 0;    
  3011. } // TextAddVoices
  3012.  
  3013. // --------------------------------------------------------------------------------------------------------------
  3014.  
  3015. static OSStatus    TextMakeWindow(WindowPtr pWindow, WindowDataPtr pData)
  3016. {
  3017. //#pragma unused(pWindow)
  3018.  
  3019.     OSStatus    anErr;
  3020.     
  3021.     anErr = TextInitData(pData);
  3022.     nrequire(anErr, InitData);
  3023.     
  3024.     anErr = TextReadDataFork(pData);
  3025.     nrequire(anErr, ReadData);
  3026.     
  3027.     TextReadResourceFork(pData);
  3028.     TextFinishTESetup(pData);
  3029.     TextCloseStationery(pData);
  3030.     
  3031.     if(gMachineInfo.haveProxyIcons)
  3032.     {
  3033.         // Set the view size of the scrollbar to the textedit view size (for proportional scrolling)
  3034.         SetControlViewSize( pData->vScroll, pData->contentRect.bottom - pData->contentRect.top );
  3035.  
  3036.         // make the document happy
  3037.         SetWindowProxyCreatorAndType( pWindow, 'ttxt', 'TEXT', kOnSystemDisk );
  3038.     }
  3039.     return noErr;
  3040.     
  3041. // EXCEPTION HANDLING
  3042. ReadData:
  3043.     TEDispose(((TextDataPtr) pData)->hTE);
  3044. InitData:
  3045.     return anErr;
  3046.     
  3047. } // TextMakeWindow
  3048.  
  3049. // --------------------------------------------------------------------------------------------------------------
  3050.  
  3051. OSStatus    TextPreflightWindow(PreflightPtr pPreflightData)
  3052. {    
  3053.     pPreflightData->continueWithOpen     = true;
  3054.     pPreflightData->wantVScroll            = true;
  3055.     pPreflightData->doZoom                = true;
  3056.     pPreflightData->makeProcPtr         = TextMakeWindow;
  3057.     if (pPreflightData->fileType != 'ttro')
  3058.         pPreflightData->openKind            = fsRdWrPerm;
  3059.     
  3060.     pPreflightData->storageSize         = sizeof(TextDataRecord);
  3061.  
  3062.     // get strings that mark the picture
  3063.     GetIndString(gPictMarker1, kTextStrings, iPictureMarker1);
  3064.     GetIndString(gPictMarker2, kTextStrings, iPictureMarker2);
  3065.  
  3066.     // do we need to account for bugs in older TEs?
  3067.     {
  3068.     long    version;
  3069.     
  3070.     if ( (Gestalt(gestaltTextEditVersion, &version) == noErr) && (version > gestaltTE5) )
  3071.         gTE6Version = true;
  3072.     }
  3073.     
  3074.     return noErr;
  3075.     
  3076. } // TextPreflightWindow
  3077.  
  3078. // --------------------------------------------------------------------------------------------------------------
  3079.  
  3080. void TextGetFileTypes(OSType * pFileTypes, OSType * pDocumentTypes, short * numTypes)
  3081. {
  3082.     pFileTypes[*numTypes]         = 'TEXT';
  3083.     pDocumentTypes[*numTypes]     = kTextWindow;
  3084.     (*numTypes)++;
  3085.         
  3086.     pFileTypes[*numTypes]         = 'ttro';
  3087.     pDocumentTypes[*numTypes]     = kTextWindow;
  3088.     (*numTypes)++;
  3089.         
  3090.     pFileTypes[*numTypes]         = 'sEXT';
  3091.     pDocumentTypes[*numTypes]     = kTextWindow;
  3092.     (*numTypes)++;
  3093.         
  3094. } // TextGetFileTypes
  3095.  
  3096. // --------------------------------------------------------------------------------------------------------------
  3097.  
  3098.  
  3099.  
  3100. // TextAddContentsMenu checks if there is a contents list and, if there
  3101. // is, creates a new menu handle for the contents list and fills it with 
  3102. // the appropriate visible items
  3103.  
  3104. void TextAddContentsMenu(WindowDataPtr pData)
  3105. {
  3106.     MenuHandle    contentsMenu;
  3107.     Str255        menuStr;
  3108.     short        totalItems;
  3109.     short        index;
  3110.     OSStatus        err;
  3111.     
  3112.     contentsMenu = GetMenuHandle(mContents);
  3113.     require(contentsMenu == nil, ContentsMenuAlreadyInstalled);
  3114.     
  3115.     // Is there a contents list?  If so, get the menu name 
  3116.     // and the number of items in the list
  3117.     
  3118.     if (TextGetContentsListItem(pData, 0, menuStr, nil, &totalItems) == noErr)
  3119.         {
  3120.         
  3121.         // create the menu and fill it with all the items
  3122.         // listed in the string list resource
  3123.         
  3124.         contentsMenu = NewMenu(mContents, menuStr);
  3125.         require(contentsMenu != nil, CantCreateContentsMenu);
  3126.         
  3127.         for (index = 1; index < totalItems; index++)
  3128.             {
  3129.             err = TextGetContentsListItem(pData, index, menuStr, nil, nil);
  3130.             require(err == noErr, CantGetItem);
  3131.             
  3132.             AppendMenu(contentsMenu, menuStr);
  3133.             }
  3134.  
  3135.         // add the menu to the menu bar, and redraw the menu bar
  3136.         InsertMenu(contentsMenu, 0);
  3137.         InvalMenuBar();
  3138.         }
  3139.     else
  3140.         {
  3141.         // no contents, do nothing
  3142.         }
  3143.     
  3144.     return;
  3145.     
  3146. // error handling
  3147. CantGetItem:
  3148. CantCreateContentsMenu:
  3149.     
  3150.     if (contentsMenu)     DisposeMenu(contentsMenu);
  3151.  
  3152. ContentsMenuAlreadyInstalled:
  3153.  
  3154.     return;
  3155.         
  3156. } // TextAddContentsMenu
  3157.  
  3158.  
  3159.  
  3160.  
  3161. // TextRemoveContentsMenu removes the contents menu, if any,
  3162. // and redraws the menu bar
  3163.  
  3164. static void TextRemoveContentsMenu(WindowDataPtr pData)
  3165. {
  3166. #pragma unused (pData)
  3167.  
  3168.     MenuHandle    contentsMenu;
  3169.     
  3170.     contentsMenu = GetMenuHandle(mContents);
  3171.     if (contentsMenu)
  3172.         {
  3173.         DeleteMenu(mContents);
  3174.         DisposeMenu(contentsMenu);
  3175.         InvalMenuBar();
  3176.         }
  3177.  
  3178. } // TextRemoveContentsMenu
  3179.  
  3180.  
  3181.  
  3182. // TextGetContentsListItem is a general utility routine for examining the
  3183. // contents menu list, returning the menu and search strings, and returning
  3184. // the total number of entries in the contents list.
  3185. //
  3186. // Pass 0 as itemNum to retrieve the strings for the contents menu title.
  3187. //
  3188. // Pass nil for menuStr, searchStr, or totalItems if you don't want that
  3189. // info returned.
  3190. //
  3191. // Returns eDocumentHasNoContentsEntries if there is no contents string list
  3192. // resource for the specified window
  3193.  
  3194. static OSStatus TextGetContentsListItem(WindowDataPtr pData, short itemNum, 
  3195.                               StringPtr menuStr, StringPtr searchStr, 
  3196.                               short *totalItems)
  3197. {
  3198.  
  3199.     OSStatus    err;
  3200.     short    oldResFile;
  3201.     short    menuItemNum;
  3202.     short    searchItemNum;
  3203.     Handle    contentsStrListHandle;
  3204.     
  3205.     // if no original resource file, don't bother
  3206.     if (pData->resRefNum == -1)    
  3207.         {
  3208.         return eDocumentHasNoContentsEntries;
  3209.         }
  3210.         
  3211.     err = noErr;
  3212.     
  3213.     oldResFile = CurResFile();
  3214.     UseResFile(pData->resRefNum);
  3215.     
  3216.     // two entries per item
  3217.     //
  3218.     // first (itemNum zero) is content menu title
  3219.     // (second -- itemNum one, search string for menu title -- is unused)
  3220.     
  3221.     menuItemNum = itemNum * 2 + 1;
  3222.     searchItemNum = menuItemNum + 1;
  3223.     
  3224.     contentsStrListHandle = Get1Resource('STR#', kContentsListID);
  3225.     if (contentsStrListHandle)
  3226.         {
  3227.         if (totalItems)    *totalItems = (*(short *)*contentsStrListHandle) / 2;
  3228.  
  3229.         if (menuStr)    GetIndString(menuStr, kContentsListID, menuItemNum);
  3230.         if (searchStr)    
  3231.             {
  3232.             GetIndString(searchStr, kContentsListID, searchItemNum);
  3233.         
  3234.             if (searchStr[0] == 0)
  3235.                 {
  3236.                 // search string was empty, so use the
  3237.                 // menu string as the search string
  3238.                 
  3239.                 GetIndString(searchStr, kContentsListID, menuItemNum);
  3240.                 }
  3241.             }
  3242.         }
  3243.     else
  3244.         {
  3245.         err = eDocumentHasNoContentsEntries;
  3246.         if (totalItems)    *totalItems = 0;
  3247.         }
  3248.     
  3249.     UseResFile(oldResFile);
  3250.     
  3251.     return err;
  3252. } // TextGetContentsListItem
  3253.  
  3254.  
  3255. // TextAdjustContentsMenu enables the items in the contents menu
  3256. //
  3257. // This routine is essentially a placeholder in case the contents
  3258. // menu really were to be dynamically enabled.
  3259.  
  3260. static OSStatus TextAdjustContentsMenu(WindowDataPtr pData)
  3261. {
  3262. #pragma unused (pData)
  3263.     
  3264.     EnableCommand(cSelectContents);
  3265.     return(noErr);
  3266.     
  3267. } // TextAdjustContentsMenu
  3268.  
  3269.